summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-11-04 22:16:15 -0300
committerPerberos <[email protected]>2011-11-04 22:16:15 -0300
commitfff4ecc82f2bcfa7427596e7ad9c3769fcab040b (patch)
treeac4f1812a7991609c9c32c776daede2d8492f7b5 /src
downloadmate-screensaver-fff4ecc82f2bcfa7427596e7ad9c3769fcab040b.tar.bz2
mate-screensaver-fff4ecc82f2bcfa7427596e7ad9c3769fcab040b.tar.xz
first commit
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am268
-rw-r--r--src/copy-theme-dialog.c545
-rw-r--r--src/copy-theme-dialog.c.orig545
-rw-r--r--src/copy-theme-dialog.h56
-rw-r--r--src/copy-theme-dialog.h.orig56
-rwxr-xr-xsrc/debug-screensaver.sh42
-rw-r--r--src/gs-auth-helper.c213
-rw-r--r--src/gs-auth-helper.c.orig213
-rw-r--r--src/gs-auth-pam.c843
-rw-r--r--src/gs-auth-pam.c.orig843
-rw-r--r--src/gs-auth-pwent.c291
-rw-r--r--src/gs-auth-pwent.c.orig291
-rw-r--r--src/gs-auth.h67
-rw-r--r--src/gs-auth.h.orig65
-rw-r--r--src/gs-debug.c163
-rw-r--r--src/gs-debug.c.orig163
-rw-r--r--src/gs-debug.h71
-rw-r--r--src/gs-fade.c987
-rw-r--r--src/gs-fade.c.orig987
-rw-r--r--src/gs-fade.h77
-rw-r--r--src/gs-fade.h.orig77
-rw-r--r--src/gs-grab-x11.c672
-rw-r--r--src/gs-grab-x11.c.orig672
-rw-r--r--src/gs-grab.h79
-rw-r--r--src/gs-grab.h.orig79
-rw-r--r--src/gs-job.c567
-rw-r--r--src/gs-job.c.orig567
-rw-r--r--src/gs-job.h69
-rw-r--r--src/gs-job.h.orig69
-rw-r--r--src/gs-listener-dbus.c2272
-rw-r--r--src/gs-listener-dbus.c.orig2272
-rw-r--r--src/gs-listener-dbus.h89
-rw-r--r--src/gs-listener-dbus.h.orig89
-rw-r--r--src/gs-lock-plug.c2050
-rw-r--r--src/gs-lock-plug.c.orig2050
-rw-r--r--src/gs-lock-plug.h82
-rw-r--r--src/gs-lock-plug.h.orig82
-rw-r--r--src/gs-manager.c2030
-rw-r--r--src/gs-manager.c.orig2030
-rw-r--r--src/gs-manager.h107
-rw-r--r--src/gs-manager.h.orig107
-rw-r--r--src/gs-marshal.list4
-rw-r--r--src/gs-monitor.c528
-rw-r--r--src/gs-monitor.c.orig528
-rw-r--r--src/gs-monitor.h57
-rw-r--r--src/gs-monitor.h.orig57
-rw-r--r--src/gs-prefs.c995
-rw-r--r--src/gs-prefs.c.orig995
-rw-r--r--src/gs-prefs.h85
-rw-r--r--src/gs-prefs.h.orig85
-rw-r--r--src/gs-theme-manager.c453
-rw-r--r--src/gs-theme-manager.c.orig453
-rw-r--r--src/gs-theme-manager.h65
-rw-r--r--src/gs-theme-manager.h.orig65
-rw-r--r--src/gs-visual-gl.c116
-rw-r--r--src/gs-visual-gl.c.orig116
-rw-r--r--src/gs-visual-gl.h32
-rw-r--r--src/gs-watcher-x11.c680
-rw-r--r--src/gs-watcher-x11.c.orig680
-rw-r--r--src/gs-watcher.h65
-rw-r--r--src/gs-watcher.h.orig65
-rw-r--r--src/gs-window-x11.c2647
-rw-r--r--src/gs-window-x11.c.orig2647
-rw-r--r--src/gs-window.h106
-rw-r--r--src/gs-window.h.orig106
-rw-r--r--src/mate-screensaver-command.c551
-rw-r--r--src/mate-screensaver-command.c.orig551
-rw-r--r--src/mate-screensaver-dialog.c665
-rw-r--r--src/mate-screensaver-dialog.c.orig665
-rw-r--r--src/mate-screensaver-gl-helper.c83
-rw-r--r--src/mate-screensaver-gl-helper.c.orig83
-rw-r--r--src/mate-screensaver-preferences.c1781
-rw-r--r--src/mate-screensaver-preferences.c.orig1781
-rw-r--r--src/mate-screensaver.c132
-rw-r--r--src/mate-screensaver.c.orig132
-rw-r--r--src/mate-screensaver.desktop.in.in14
-rw-r--r--src/mate-screensaver.h32
-rw-r--r--src/setuid.c247
-rw-r--r--src/setuid.c.orig247
-rw-r--r--src/setuid.h25
-rw-r--r--src/setuid.h.orig25
-rw-r--r--src/subprocs.c166
-rw-r--r--src/subprocs.c.orig166
-rw-r--r--src/subprocs.h39
-rw-r--r--src/subprocs.h.orig39
-rw-r--r--src/test-fade.c134
-rw-r--r--src/test-fade.c.orig134
-rw-r--r--src/test-passwd.c288
-rw-r--r--src/test-passwd.c.orig288
-rw-r--r--src/test-watcher.c106
-rw-r--r--src/test-watcher.c.orig106
-rw-r--r--src/test-window.c180
-rw-r--r--src/test-window.c.orig180
93 files changed, 43367 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..1de819f
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,268 @@
+## We require new-style dependency handling.
+AUTOMAKE_OPTIONS = 1.7
+
+NULL =
+
+saverdir = $(libexecdir)/mate-screensaver
+themesdir = $(pkgdatadir)/themes
+
+INCLUDES = \
+ -I. \
+ -I$(srcdir) \
+ -DMATEMENU_I_KNOW_THIS_IS_UNSTABLE \
+ $(MATE_SCREENSAVER_CFLAGS) \
+ $(MATE_SCREENSAVER_DIALOG_CFLAGS) \
+ $(MATE_SCREENSAVER_CAPPLET_CFLAGS) \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ -DPREFIX=\""$(prefix)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ -DSYSCONFDIR=\""$(sysconfdir)"\" \
+ -DMATELOCALEDIR=\""$(datadir)/locale"\" \
+ -DSAVERDIR=\""$(saverdir)"\" \
+ -DTHEMESDIR=\""$(themesdir)"\" \
+ -DGTKBUILDERDIR=\"$(pkgdatadir)\" \
+ -DPAM_SERVICE_NAME=\""mate-screensaver"\" \
+ $(WARN_CFLAGS) \
+ $(AUTH_CFLAGS) \
+ $(DEBUG_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(LIBMATEKBDUI_CFLAGS) \
+ $(LIBMATENOTIFY_CFLAGS) \
+ $(NULL)
+
+bin_PROGRAMS = \
+ mate-screensaver \
+ mate-screensaver-command \
+ mate-screensaver-preferences \
+ $(NULL)
+
+libexec_PROGRAMS = \
+ mate-screensaver-dialog \
+ mate-screensaver-gl-helper \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ test-fade \
+ test-passwd \
+ test-watcher \
+ test-window \
+ $(NULL)
+
+desktopdir = $(sysconfdir)/xdg/autostart
+desktop_in_files = mate-screensaver.desktop.in
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+@INTLTOOL_DESKTOP_RULE@
+
+mate_screensaver_command_SOURCES = \
+ mate-screensaver-command.c \
+ $(NULL)
+
+mate_screensaver_command_LDADD = \
+ $(MATE_SCREENSAVER_COMMAND_LIBS) \
+ $(NULL)
+
+AUTH_SOURCES = \
+ gs-auth.h \
+ gs-auth-@[email protected] \
+ $(NULL)
+
+test_fade_SOURCES = \
+ test-fade.c \
+ gs-fade.c \
+ gs-fade.h \
+ gs-debug.c \
+ gs-debug.h \
+ $(NULL)
+
+test_fade_LDADD = \
+ $(MATE_SCREENSAVER_LIBS) \
+ $(SAVER_LIBS) \
+ $(NULL)
+
+test_passwd_SOURCES = \
+ test-passwd.c \
+ $(AUTH_SOURCES) \
+ setuid.c \
+ setuid.h \
+ subprocs.c \
+ subprocs.h \
+ $(NULL)
+
+test_passwd_LDADD = \
+ $(MATE_SCREENSAVER_DIALOG_LIBS)\
+ $(AUTH_LIBS) \
+ $(NULL)
+
+test_watcher_SOURCES = \
+ test-watcher.c \
+ gs-watcher.h \
+ gs-watcher-x11.c \
+ gs-marshal.c \
+ gs-marshal.h \
+ gs-debug.c \
+ gs-debug.h \
+ $(NULL)
+
+test_watcher_LDADD = \
+ $(MATE_SCREENSAVER_LIBS) \
+ $(SAVER_LIBS) \
+ $(NULL)
+
+test_window_SOURCES = \
+ test-window.c \
+ gs-window.h \
+ gs-window-x11.c \
+ gs-grab-x11.c \
+ gs-grab.h \
+ gs-marshal.c \
+ gs-marshal.h \
+ gs-debug.c \
+ gs-debug.h \
+ subprocs.c \
+ subprocs.h \
+ $(NULL)
+
+test_window_LDADD = \
+ $(MATE_SCREENSAVER_LIBS) \
+ $(SAVER_LIBS) \
+ $(NULL)
+
+mate_screensaver_dialog_SOURCES = \
+ mate-screensaver-dialog.c \
+ gs-lock-plug.c \
+ gs-lock-plug.h \
+ gs-debug.c \
+ gs-debug.h \
+ setuid.c \
+ setuid.h \
+ subprocs.c \
+ subprocs.h \
+ $(AUTH_SOURCES) \
+ $(NULL)
+
+EXTRA_mate_screensaver_dialog_SOURCES = \
+ gs-auth-pam.c \
+ gs-auth-helper.c \
+ gs-auth-pwent.c \
+ $(NULL)
+
+mate_screensaver_dialog_LDADD = \
+ $(MATE_SCREENSAVER_DIALOG_LIBS)\
+ $(SAVER_LIBS) \
+ $(AUTH_LIBS) \
+ $(LIBMATEKBDUI_LIBS) \
+ $(LIBMATENOTIFY_LIBS) \
+ $(NULL)
+
+BUILT_SOURCES = \
+ gs-marshal.c \
+ gs-marshal.h \
+ $(NULL)
+
+gs-marshal.c: gs-marshal.list
+ echo "#include \"gs-marshal.h\"" > $@ && \
+ @GLIB_GENMARSHAL@ $< --prefix=gs_marshal --body >> $@
+
+gs-marshal.h: gs-marshal.list
+ @GLIB_GENMARSHAL@ $< --prefix=gs_marshal --header > $@
+
+mate_screensaver_SOURCES = \
+ mate-screensaver.c \
+ mate-screensaver.h \
+ gs-monitor.c \
+ gs-monitor.h \
+ gs-watcher-x11.c \
+ gs-watcher.h \
+ gs-listener-dbus.c \
+ gs-listener-dbus.h \
+ gs-manager.c \
+ gs-manager.h \
+ gs-window-x11.c \
+ gs-window.h \
+ gs-prefs.c \
+ gs-prefs.h \
+ gs-theme-manager.c \
+ gs-theme-manager.h \
+ gs-job.c \
+ gs-job.h \
+ gs-debug.c \
+ gs-debug.h \
+ subprocs.c \
+ subprocs.h \
+ gs-grab-x11.c \
+ gs-grab.h \
+ gs-fade.c \
+ gs-fade.h \
+ $(BUILT_SOURCES) \
+ $(NULL)
+
+mate_screensaver_gl_helper_LDADD = \
+ $(MATE_SCREENSAVER_LIBS) \
+ $(GL_LIBS) \
+ $(NULL)
+
+mate_screensaver_gl_helper_SOURCES = \
+ mate-screensaver-gl-helper.c \
+ gs-visual-gl.c \
+ gs-visual-gl.h \
+ $(NULL)
+
+mate_screensaver_LDADD = \
+ $(MATE_SCREENSAVER_LIBS) \
+ $(SAVER_LIBS) \
+ $(NULL)
+
+mate_screensaver_LDFLAGS = -export-dynamic
+
+mate_screensaver_preferences_SOURCES = \
+ mate-screensaver-preferences.c \
+ copy-theme-dialog.c \
+ copy-theme-dialog.h \
+ gs-theme-manager.c \
+ gs-theme-manager.h \
+ gs-job.c \
+ gs-job.h \
+ gs-debug.c \
+ gs-debug.h \
+ subprocs.c \
+ subprocs.h \
+ $(NULL)
+
+mate_screensaver_preferences_LDADD = \
+ $(MATE_SCREENSAVER_CAPPLET_LIBS) \
+ $(NULL)
+
+EXTRA_DIST = \
+ debug-screensaver.sh \
+ gs-marshal.list \
+ mate-screensaver.desktop.in \
+ $(NULL)
+
+CLEANFILES = \
+ $(desktop_DATA) \
+ mate-screensaver.desktop.in \
+ $(BUILT_SOURCES)
+
+MAINTAINERCLEANFILES = \
+ *~ \
+ Makefile.in
+
+install-exec-hook:
+ @if [ "x@NEED_SETUID@" = "xyes" ]; then \
+ echo "***" ; \
+ echo "*** Warning: mate-screensaver has been compiled with support for" ; \
+ echo "*** shadow passwords. If your system actually uses shadow" ; \
+ echo "*** passwords then it must be installed as a setuid root" ; \
+ echo "*** program in order for locking to work. To do this, you" ; \
+ echo "*** must run:" ; \
+ echo "***" ; \
+ echo "*** chown root $(DESTDIR)$(libexecdir)/mate-screensaver-dialog" ; \
+ echo "*** chmod +s $(DESTDIR)$(libexecdir)/mate-screensaver-dialog" ; \
+ echo "***" ; \
+ echo "*** For now, it will be installed non-setuid, which" ; \
+ echo "*** means that locking might not work." ; \
+ echo "***" ; \
+ fi
diff --git a/src/copy-theme-dialog.c b/src/copy-theme-dialog.c
new file mode 100644
index 0000000..ed7808d
--- /dev/null
+++ b/src/copy-theme-dialog.c
@@ -0,0 +1,545 @@
+/* copy-theme-dialog.c
+ * Copyright (C) 2008 John Millikin <[email protected]>
+ *
+ * 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, 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 <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "copy-theme-dialog.h"
+
+static void
+copy_theme_dialog_class_init (CopyThemeDialogClass *klass);
+static void
+copy_theme_dialog_init (CopyThemeDialog *dlg);
+static void
+add_file_to_dialog (gpointer data, gpointer user_data);
+static void
+single_copy_complete (GObject *source_object, GAsyncResult *res,
+ gpointer user_data);
+static void
+copy_theme_dialog_copy_next (CopyThemeDialog *dialog);
+static void
+copy_theme_dialog_cancel (CopyThemeDialog *dialog);
+static void
+copy_theme_dialog_finalize (GObject *obj);
+static void
+copy_theme_dialog_update_num_files (CopyThemeDialog *dlg);
+static void
+copy_theme_dialog_response (GtkDialog *dialog, gint response_id);
+static void
+eel_gtk_label_make_bold (GtkLabel *label);
+static void
+create_titled_label (GtkTable *table,
+ int row,
+ GtkWidget **title_widget,
+ GtkWidget **label_text_widget);
+
+static GObjectClass *parent_class = NULL;
+
+enum
+{
+ CANCELLED = 0,
+ COMPLETE,
+ SIGNAL_COUNT
+};
+
+struct _CopyThemeDialogPrivate
+{
+ GtkWidget *progress;
+ GtkWidget *status;
+ GtkWidget *current;
+ GtkWidget *from;
+ GtkWidget *to;
+
+ GFile *theme_dir;
+ GSList *all_files, *file;
+ GSList *all_basenames, *basename;
+ guint index;
+ guint total_files;
+ GCancellable *cancellable;
+};
+
+guint signals[SIGNAL_COUNT] = {0, 0};
+
+GType
+copy_theme_dialog_get_type (void)
+{
+ static GType copy_theme_dialog_type = 0;
+
+ if (!copy_theme_dialog_type)
+ {
+ static GTypeInfo copy_theme_dialog_info =
+ {
+ sizeof (CopyThemeDialogClass),
+ NULL, /* GBaseInitFunc */
+ NULL, /* GBaseFinalizeFunc */
+ (GClassInitFunc) copy_theme_dialog_class_init,
+ NULL, /* GClassFinalizeFunc */
+ NULL, /* data */
+ sizeof (CopyThemeDialog),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) copy_theme_dialog_init,
+ NULL
+ };
+
+ copy_theme_dialog_type = g_type_register_static (GTK_TYPE_DIALOG,
+ "CopyThemeDialog",
+ &copy_theme_dialog_info,
+ 0);
+ }
+
+ return copy_theme_dialog_type;
+}
+
+static void
+copy_theme_dialog_class_init (CopyThemeDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (CopyThemeDialogPrivate));
+
+ klass->cancelled = copy_theme_dialog_cancel;
+ object_class->finalize = copy_theme_dialog_finalize;
+
+ GTK_DIALOG_CLASS (klass)->response = copy_theme_dialog_response;
+
+ signals[CANCELLED] = g_signal_new ("cancelled",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (CopyThemeDialogClass, cancelled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[COMPLETE] = g_signal_new ("complete",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CopyThemeDialogClass, complete),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ parent_class = g_type_class_peek_parent (klass);
+}
+
+GtkWidget*
+copy_theme_dialog_new (GList *files)
+{
+ GtkWidget *dialog;
+ CopyThemeDialogPrivate *priv;
+
+ dialog = GTK_WIDGET (g_object_new (COPY_THEME_DIALOG_TYPE, NULL));
+ priv = COPY_THEME_DIALOG (dialog)->priv;
+ priv->index = 0;
+ priv->total_files = 0;
+ priv->all_files = NULL;
+ priv->all_basenames = NULL;
+
+ g_list_foreach (files, add_file_to_dialog, dialog);
+
+ priv->file = priv->all_files;
+ priv->basename = priv->all_basenames;
+
+ return dialog;
+}
+
+static gboolean
+copy_finished (CopyThemeDialog *dialog)
+{
+ return (g_cancellable_is_cancelled (dialog->priv->cancellable) ||
+ dialog->priv->file == NULL);
+}
+
+static void
+copy_theme_dialog_init (CopyThemeDialog *dlg)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *progress_vbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ char *markup;
+ gchar *theme_dir_path;
+
+ dlg->priv = G_TYPE_INSTANCE_GET_PRIVATE (dlg, COPY_THEME_DIALOG_TYPE,
+ CopyThemeDialogPrivate);
+
+ /* Find and, if needed, create the directory for storing themes */
+ theme_dir_path = g_build_filename (g_get_user_data_dir (),
+ "applications", "screensavers",
+ NULL);
+ dlg->priv->theme_dir = g_file_new_for_path (theme_dir_path);
+ g_mkdir_with_parents (theme_dir_path, S_IRWXU);
+ g_free (theme_dir_path);
+
+ /* For cancelling async I/O operations */
+ dlg->priv->cancellable = g_cancellable_new ();
+
+ /* GUI settings */
+ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox),
+ 4);
+ gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dlg)->vbox), 4);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), vbox, TRUE, TRUE, 0);
+
+ dlg->priv->status = gtk_label_new ("");
+ markup = g_strdup_printf ("<big><b>%s</b></big>", _("Copying files"));
+ gtk_label_set_markup (GTK_LABEL (dlg->priv->status), markup);
+ g_free (markup);
+
+ gtk_misc_set_alignment (GTK_MISC (dlg->priv->status), 0.0, 0.0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), dlg->priv->status, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+
+ create_titled_label (GTK_TABLE (table), 0,
+ &label,
+ &dlg->priv->from);
+ gtk_label_set_text (GTK_LABEL (label), _("From:"));
+ create_titled_label (GTK_TABLE (table), 1,
+ &label,
+ &dlg->priv->to);
+ gtk_label_set_text (GTK_LABEL (label), _("To:"));
+
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (table), FALSE, FALSE, 0);
+
+ progress_vbox = gtk_vbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), progress_vbox, FALSE, FALSE, 0);
+
+ dlg->priv->progress = gtk_progress_bar_new ();
+ gtk_box_pack_start (GTK_BOX (progress_vbox),
+ dlg->priv->progress, FALSE, FALSE, 0);
+
+ dlg->priv->current = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (progress_vbox),
+ dlg->priv->current, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (dlg->priv->current), 0.0, 0.5);
+
+ gtk_dialog_add_button (GTK_DIALOG (dlg),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+ gtk_window_set_title (GTK_WINDOW (dlg),
+ _("Copying themes"));
+ gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dlg), 6);
+
+ gtk_widget_show_all (GTK_DIALOG (dlg)->vbox);
+}
+
+static void
+add_file_to_dialog (gpointer data, gpointer user_data)
+{
+ CopyThemeDialogPrivate *priv;
+ GFile *file;
+ gchar *basename = NULL, *raw_basename;
+
+ priv = COPY_THEME_DIALOG (user_data)->priv;
+ file = G_FILE (data);
+
+ raw_basename = g_file_get_basename (file);
+ if (g_str_has_suffix (raw_basename, ".desktop"))
+ {
+ /* FIXME: validate key file? */
+ basename = g_strndup (raw_basename,
+ /* 8 = strlen (".desktop") */
+ strlen (raw_basename) - 8);
+ }
+ g_free (raw_basename);
+
+ if (basename)
+ {
+ g_object_ref (file);
+ priv->all_files = g_slist_append (priv->all_files, file);
+ priv->all_basenames = g_slist_append (priv->all_basenames, basename);
+ priv->total_files++;
+ }
+
+ else
+ {
+ GtkWidget *dialog;
+ gchar *uri;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (user_data),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Invalid screensaver theme"));
+ uri = g_file_get_uri (file);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("%s does not appear to be a valid screensaver theme."),
+ uri);
+ g_free (uri);
+ gtk_window_set_title (GTK_WINDOW (dialog), "");
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "preferences-desktop-screensaver");
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ }
+}
+
+static void
+single_copy_complete (GObject *source_object, GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gboolean should_continue = FALSE;
+ CopyThemeDialog *dialog = COPY_THEME_DIALOG (user_data);
+
+ if (g_file_copy_finish (G_FILE (source_object), res, &error))
+ {
+ should_continue = TRUE;
+ }
+
+ else
+ {
+ /* If the file already exists, generate a new random name
+ * and try again.
+ **/
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ GFile *file, *destination;
+ gchar *basename, *full_basename;
+ g_error_free (error);
+
+ file = G_FILE (dialog->priv->file->data);
+ basename = (gchar *) (dialog->priv->basename->data);
+
+ g_return_if_fail (file != NULL);
+ g_return_if_fail (basename != NULL);
+
+ full_basename = g_strdup_printf ("%s-%u.desktop",
+ basename,
+ g_random_int ());
+ destination = g_file_get_child (dialog->priv->theme_dir,
+ full_basename);
+ g_free (full_basename);
+
+ g_file_copy_async (file, destination, G_FILE_COPY_NONE,
+ G_PRIORITY_DEFAULT,
+ dialog->priv->cancellable,
+ NULL, NULL,
+ single_copy_complete, dialog);
+ }
+
+ else
+ {
+ if (g_error_matches (error, G_IO_ERROR,
+ G_IO_ERROR_CANCELLED))
+ {
+ /* User has cancelled the theme copy */
+ g_signal_emit (G_OBJECT (dialog),
+ signals[CANCELLED],
+ 0, NULL);
+ }
+
+ else
+ {
+ /* Some other error occurred, ignore and
+ * try to copy remaining files
+ **/
+ should_continue = TRUE;
+ }
+
+ g_error_free (error);
+ }
+ }
+
+ /* Update informational widgets and, if needed, signal
+ * copy completion.
+ **/
+ if (should_continue)
+ {
+ dialog->priv->index++;
+ dialog->priv->file = dialog->priv->file->next;
+ dialog->priv->basename = dialog->priv->basename->next;
+ copy_theme_dialog_update_num_files (dialog);
+ copy_theme_dialog_copy_next (dialog);
+ }
+}
+
+/* Try to copy the theme file to the user's screensaver directory.
+ * If a file with the given name already exists, the error will be
+ * caught later and the copy re-attempted with a random value
+ * appended to the filename.
+**/
+static void
+copy_theme_dialog_copy_next (CopyThemeDialog *dialog)
+{
+ GFile *file, *destination;
+ gchar *basename, *full_basename;
+
+ if (copy_finished (dialog))
+ {
+ g_signal_emit (G_OBJECT (dialog), signals[COMPLETE],
+ 0, NULL);
+ return;
+ }
+
+ file = G_FILE (dialog->priv->file->data);
+ basename = (gchar *) (dialog->priv->basename->data);
+
+ g_return_if_fail (file != NULL);
+ g_return_if_fail (basename != NULL);
+
+ full_basename = g_strdup_printf ("%s.desktop", basename);
+ destination = g_file_get_child (dialog->priv->theme_dir, full_basename);
+ g_free (full_basename);
+
+ g_file_copy_async (file, destination, G_FILE_COPY_NONE,
+ G_PRIORITY_DEFAULT, dialog->priv->cancellable,
+ NULL, NULL, single_copy_complete, dialog);
+}
+
+static gboolean
+timeout_display_dialog (gpointer data)
+{
+ if (IS_COPY_THEME_DIALOG (data))
+ {
+ CopyThemeDialog *dialog = COPY_THEME_DIALOG (data);
+ if (!copy_finished (dialog))
+ {
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (copy_theme_dialog_response),
+ dialog);
+ }
+ }
+ return FALSE;
+}
+
+void
+copy_theme_dialog_begin (CopyThemeDialog *dialog)
+{
+ gtk_widget_hide (GTK_WIDGET (dialog));
+
+ /* If the copy operation takes more than half a second to
+ * complete, display the dialog.
+ **/
+ g_timeout_add (500, timeout_display_dialog, dialog);
+
+ copy_theme_dialog_copy_next (dialog);
+}
+
+static void
+copy_theme_dialog_cancel (CopyThemeDialog *dialog)
+{
+ g_cancellable_cancel (dialog->priv->cancellable);
+}
+
+static void
+copy_theme_dialog_finalize (GObject *obj)
+{
+ CopyThemeDialog *dlg = COPY_THEME_DIALOG (obj);
+
+ g_object_unref (dlg->priv->theme_dir);
+ g_slist_foreach (dlg->priv->all_files, (GFunc) (g_object_unref), NULL);
+ g_slist_free (dlg->priv->all_files);
+ g_slist_foreach (dlg->priv->all_basenames, (GFunc) (g_free), NULL);
+ g_slist_free (dlg->priv->all_basenames);
+ g_object_unref (dlg->priv->cancellable);
+
+ if (parent_class->finalize)
+ parent_class->finalize (G_OBJECT (dlg));
+}
+
+static void
+copy_theme_dialog_update_num_files (CopyThemeDialog *dlg)
+{
+ gchar *str = g_strdup_printf (_("Copying file: %u of %u"),
+ dlg->priv->index, dlg->priv->total_files);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dlg->priv->progress), str);
+ g_free (str);
+}
+
+static void
+copy_theme_dialog_response (GtkDialog *dialog, gint response_id)
+{
+ g_cancellable_cancel (COPY_THEME_DIALOG (dialog)->priv->cancellable);
+}
+
+/**
+ * eel_gtk_label_make_bold.
+ *
+ * Switches the font of label to a bold equivalent.
+ * @label: The label.
+ **/
+static void
+eel_gtk_label_make_bold (GtkLabel *label)
+{
+ PangoFontDescription *font_desc;
+
+ font_desc = pango_font_description_new ();
+
+ pango_font_description_set_weight (font_desc,
+ PANGO_WEIGHT_BOLD);
+
+ /* This will only affect the weight of the font, the rest is
+ * from the current state of the widget, which comes from the
+ * theme or user prefs, since the font desc only has the
+ * weight flag turned on.
+ */
+ gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
+
+ pango_font_description_free (font_desc);
+}
+
+/* from caja */
+static void
+create_titled_label (GtkTable *table,
+ int row,
+ GtkWidget **title_widget,
+ GtkWidget **label_text_widget)
+{
+ *title_widget = gtk_label_new ("");
+ eel_gtk_label_make_bold (GTK_LABEL (*title_widget));
+ gtk_misc_set_alignment (GTK_MISC (*title_widget), 1, 0);
+ gtk_table_attach (table, *title_widget,
+ 0, 1,
+ row, row + 1,
+ GTK_FILL, 0,
+ 0, 0);
+ gtk_widget_show (*title_widget);
+
+ *label_text_widget = gtk_label_new ("");
+ gtk_label_set_ellipsize (GTK_LABEL (*label_text_widget), PANGO_ELLIPSIZE_END);
+ gtk_table_attach (table, *label_text_widget,
+ 1, 2,
+ row, row + 1,
+ GTK_FILL | GTK_EXPAND, 0,
+ 0, 0);
+ gtk_widget_show (*label_text_widget);
+ gtk_misc_set_alignment (GTK_MISC (*label_text_widget), 0, 0);
+}
diff --git a/src/copy-theme-dialog.c.orig b/src/copy-theme-dialog.c.orig
new file mode 100644
index 0000000..d8150eb
--- /dev/null
+++ b/src/copy-theme-dialog.c.orig
@@ -0,0 +1,545 @@
+/* copy-theme-dialog.c
+ * Copyright (C) 2008 John Millikin <[email protected]>
+ *
+ * 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, 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 <limits.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "copy-theme-dialog.h"
+
+static void
+copy_theme_dialog_class_init (CopyThemeDialogClass *klass);
+static void
+copy_theme_dialog_init (CopyThemeDialog *dlg);
+static void
+add_file_to_dialog (gpointer data, gpointer user_data);
+static void
+single_copy_complete (GObject *source_object, GAsyncResult *res,
+ gpointer user_data);
+static void
+copy_theme_dialog_copy_next (CopyThemeDialog *dialog);
+static void
+copy_theme_dialog_cancel (CopyThemeDialog *dialog);
+static void
+copy_theme_dialog_finalize (GObject *obj);
+static void
+copy_theme_dialog_update_num_files (CopyThemeDialog *dlg);
+static void
+copy_theme_dialog_response (GtkDialog *dialog, gint response_id);
+static void
+eel_gtk_label_make_bold (GtkLabel *label);
+static void
+create_titled_label (GtkTable *table,
+ int row,
+ GtkWidget **title_widget,
+ GtkWidget **label_text_widget);
+
+static GObjectClass *parent_class = NULL;
+
+enum
+{
+ CANCELLED = 0,
+ COMPLETE,
+ SIGNAL_COUNT
+};
+
+struct _CopyThemeDialogPrivate
+{
+ GtkWidget *progress;
+ GtkWidget *status;
+ GtkWidget *current;
+ GtkWidget *from;
+ GtkWidget *to;
+
+ GFile *theme_dir;
+ GSList *all_files, *file;
+ GSList *all_basenames, *basename;
+ guint index;
+ guint total_files;
+ GCancellable *cancellable;
+};
+
+guint signals[SIGNAL_COUNT] = {0, 0};
+
+GType
+copy_theme_dialog_get_type (void)
+{
+ static GType copy_theme_dialog_type = 0;
+
+ if (!copy_theme_dialog_type)
+ {
+ static GTypeInfo copy_theme_dialog_info =
+ {
+ sizeof (CopyThemeDialogClass),
+ NULL, /* GBaseInitFunc */
+ NULL, /* GBaseFinalizeFunc */
+ (GClassInitFunc) copy_theme_dialog_class_init,
+ NULL, /* GClassFinalizeFunc */
+ NULL, /* data */
+ sizeof (CopyThemeDialog),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) copy_theme_dialog_init,
+ NULL
+ };
+
+ copy_theme_dialog_type = g_type_register_static (GTK_TYPE_DIALOG,
+ "CopyThemeDialog",
+ &copy_theme_dialog_info,
+ 0);
+ }
+
+ return copy_theme_dialog_type;
+}
+
+static void
+copy_theme_dialog_class_init (CopyThemeDialogClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (CopyThemeDialogPrivate));
+
+ klass->cancelled = copy_theme_dialog_cancel;
+ object_class->finalize = copy_theme_dialog_finalize;
+
+ GTK_DIALOG_CLASS (klass)->response = copy_theme_dialog_response;
+
+ signals[CANCELLED] = g_signal_new ("cancelled",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (CopyThemeDialogClass, cancelled),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[COMPLETE] = g_signal_new ("complete",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (CopyThemeDialogClass, complete),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ parent_class = g_type_class_peek_parent (klass);
+}
+
+GtkWidget*
+copy_theme_dialog_new (GList *files)
+{
+ GtkWidget *dialog;
+ CopyThemeDialogPrivate *priv;
+
+ dialog = GTK_WIDGET (g_object_new (COPY_THEME_DIALOG_TYPE, NULL));
+ priv = COPY_THEME_DIALOG (dialog)->priv;
+ priv->index = 0;
+ priv->total_files = 0;
+ priv->all_files = NULL;
+ priv->all_basenames = NULL;
+
+ g_list_foreach (files, add_file_to_dialog, dialog);
+
+ priv->file = priv->all_files;
+ priv->basename = priv->all_basenames;
+
+ return dialog;
+}
+
+static gboolean
+copy_finished (CopyThemeDialog *dialog)
+{
+ return (g_cancellable_is_cancelled (dialog->priv->cancellable) ||
+ dialog->priv->file == NULL);
+}
+
+static void
+copy_theme_dialog_init (CopyThemeDialog *dlg)
+{
+ GtkWidget *vbox;
+ GtkWidget *hbox;
+ GtkWidget *progress_vbox;
+ GtkWidget *table;
+ GtkWidget *label;
+ char *markup;
+ gchar *theme_dir_path;
+
+ dlg->priv = G_TYPE_INSTANCE_GET_PRIVATE (dlg, COPY_THEME_DIALOG_TYPE,
+ CopyThemeDialogPrivate);
+
+ /* Find and, if needed, create the directory for storing themes */
+ theme_dir_path = g_build_filename (g_get_user_data_dir (),
+ "applications", "screensavers",
+ NULL);
+ dlg->priv->theme_dir = g_file_new_for_path (theme_dir_path);
+ g_mkdir_with_parents (theme_dir_path, S_IRWXU);
+ g_free (theme_dir_path);
+
+ /* For cancelling async I/O operations */
+ dlg->priv->cancellable = g_cancellable_new ();
+
+ /* GUI settings */
+ gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox),
+ 4);
+ gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dlg)->vbox), 4);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), vbox, TRUE, TRUE, 0);
+
+ dlg->priv->status = gtk_label_new ("");
+ markup = g_strdup_printf ("<big><b>%s</b></big>", _("Copying files"));
+ gtk_label_set_markup (GTK_LABEL (dlg->priv->status), markup);
+ g_free (markup);
+
+ gtk_misc_set_alignment (GTK_MISC (dlg->priv->status), 0.0, 0.0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), dlg->priv->status, FALSE, FALSE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 4);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 4);
+
+ create_titled_label (GTK_TABLE (table), 0,
+ &label,
+ &dlg->priv->from);
+ gtk_label_set_text (GTK_LABEL (label), _("From:"));
+ create_titled_label (GTK_TABLE (table), 1,
+ &label,
+ &dlg->priv->to);
+ gtk_label_set_text (GTK_LABEL (label), _("To:"));
+
+ gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (table), FALSE, FALSE, 0);
+
+ progress_vbox = gtk_vbox_new (TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), progress_vbox, FALSE, FALSE, 0);
+
+ dlg->priv->progress = gtk_progress_bar_new ();
+ gtk_box_pack_start (GTK_BOX (progress_vbox),
+ dlg->priv->progress, FALSE, FALSE, 0);
+
+ dlg->priv->current = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (progress_vbox),
+ dlg->priv->current, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (dlg->priv->current), 0.0, 0.5);
+
+ gtk_dialog_add_button (GTK_DIALOG (dlg),
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+
+ gtk_window_set_title (GTK_WINDOW (dlg),
+ _("Copying themes"));
+ gtk_dialog_set_has_separator (GTK_DIALOG (dlg), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (dlg), 6);
+
+ gtk_widget_show_all (GTK_DIALOG (dlg)->vbox);
+}
+
+static void
+add_file_to_dialog (gpointer data, gpointer user_data)
+{
+ CopyThemeDialogPrivate *priv;
+ GFile *file;
+ gchar *basename = NULL, *raw_basename;
+
+ priv = COPY_THEME_DIALOG (user_data)->priv;
+ file = G_FILE (data);
+
+ raw_basename = g_file_get_basename (file);
+ if (g_str_has_suffix (raw_basename, ".desktop"))
+ {
+ /* FIXME: validate key file? */
+ basename = g_strndup (raw_basename,
+ /* 8 = strlen (".desktop") */
+ strlen (raw_basename) - 8);
+ }
+ g_free (raw_basename);
+
+ if (basename)
+ {
+ g_object_ref (file);
+ priv->all_files = g_slist_append (priv->all_files, file);
+ priv->all_basenames = g_slist_append (priv->all_basenames, basename);
+ priv->total_files++;
+ }
+
+ else
+ {
+ GtkWidget *dialog;
+ gchar *uri;
+
+ dialog = gtk_message_dialog_new (GTK_WINDOW (user_data),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Invalid screensaver theme"));
+ uri = g_file_get_uri (file);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("%s does not appear to be a valid screensaver theme."),
+ uri);
+ g_free (uri);
+ gtk_window_set_title (GTK_WINDOW (dialog), "");
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "preferences-desktop-screensaver");
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ }
+}
+
+static void
+single_copy_complete (GObject *source_object, GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gboolean should_continue = FALSE;
+ CopyThemeDialog *dialog = COPY_THEME_DIALOG (user_data);
+
+ if (g_file_copy_finish (G_FILE (source_object), res, &error))
+ {
+ should_continue = TRUE;
+ }
+
+ else
+ {
+ /* If the file already exists, generate a new random name
+ * and try again.
+ **/
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
+ {
+ GFile *file, *destination;
+ gchar *basename, *full_basename;
+ g_error_free (error);
+
+ file = G_FILE (dialog->priv->file->data);
+ basename = (gchar *) (dialog->priv->basename->data);
+
+ g_return_if_fail (file != NULL);
+ g_return_if_fail (basename != NULL);
+
+ full_basename = g_strdup_printf ("%s-%u.desktop",
+ basename,
+ g_random_int ());
+ destination = g_file_get_child (dialog->priv->theme_dir,
+ full_basename);
+ g_free (full_basename);
+
+ g_file_copy_async (file, destination, G_FILE_COPY_NONE,
+ G_PRIORITY_DEFAULT,
+ dialog->priv->cancellable,
+ NULL, NULL,
+ single_copy_complete, dialog);
+ }
+
+ else
+ {
+ if (g_error_matches (error, G_IO_ERROR,
+ G_IO_ERROR_CANCELLED))
+ {
+ /* User has cancelled the theme copy */
+ g_signal_emit (G_OBJECT (dialog),
+ signals[CANCELLED],
+ 0, NULL);
+ }
+
+ else
+ {
+ /* Some other error occurred, ignore and
+ * try to copy remaining files
+ **/
+ should_continue = TRUE;
+ }
+
+ g_error_free (error);
+ }
+ }
+
+ /* Update informational widgets and, if needed, signal
+ * copy completion.
+ **/
+ if (should_continue)
+ {
+ dialog->priv->index++;
+ dialog->priv->file = dialog->priv->file->next;
+ dialog->priv->basename = dialog->priv->basename->next;
+ copy_theme_dialog_update_num_files (dialog);
+ copy_theme_dialog_copy_next (dialog);
+ }
+}
+
+/* Try to copy the theme file to the user's screensaver directory.
+ * If a file with the given name already exists, the error will be
+ * caught later and the copy re-attempted with a random value
+ * appended to the filename.
+**/
+static void
+copy_theme_dialog_copy_next (CopyThemeDialog *dialog)
+{
+ GFile *file, *destination;
+ gchar *basename, *full_basename;
+
+ if (copy_finished (dialog))
+ {
+ g_signal_emit (G_OBJECT (dialog), signals[COMPLETE],
+ 0, NULL);
+ return;
+ }
+
+ file = G_FILE (dialog->priv->file->data);
+ basename = (gchar *) (dialog->priv->basename->data);
+
+ g_return_if_fail (file != NULL);
+ g_return_if_fail (basename != NULL);
+
+ full_basename = g_strdup_printf ("%s.desktop", basename);
+ destination = g_file_get_child (dialog->priv->theme_dir, full_basename);
+ g_free (full_basename);
+
+ g_file_copy_async (file, destination, G_FILE_COPY_NONE,
+ G_PRIORITY_DEFAULT, dialog->priv->cancellable,
+ NULL, NULL, single_copy_complete, dialog);
+}
+
+static gboolean
+timeout_display_dialog (gpointer data)
+{
+ if (IS_COPY_THEME_DIALOG (data))
+ {
+ CopyThemeDialog *dialog = COPY_THEME_DIALOG (data);
+ if (!copy_finished (dialog))
+ {
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (copy_theme_dialog_response),
+ dialog);
+ }
+ }
+ return FALSE;
+}
+
+void
+copy_theme_dialog_begin (CopyThemeDialog *dialog)
+{
+ gtk_widget_hide (GTK_WIDGET (dialog));
+
+ /* If the copy operation takes more than half a second to
+ * complete, display the dialog.
+ **/
+ g_timeout_add (500, timeout_display_dialog, dialog);
+
+ copy_theme_dialog_copy_next (dialog);
+}
+
+static void
+copy_theme_dialog_cancel (CopyThemeDialog *dialog)
+{
+ g_cancellable_cancel (dialog->priv->cancellable);
+}
+
+static void
+copy_theme_dialog_finalize (GObject *obj)
+{
+ CopyThemeDialog *dlg = COPY_THEME_DIALOG (obj);
+
+ g_object_unref (dlg->priv->theme_dir);
+ g_slist_foreach (dlg->priv->all_files, (GFunc) (g_object_unref), NULL);
+ g_slist_free (dlg->priv->all_files);
+ g_slist_foreach (dlg->priv->all_basenames, (GFunc) (g_free), NULL);
+ g_slist_free (dlg->priv->all_basenames);
+ g_object_unref (dlg->priv->cancellable);
+
+ if (parent_class->finalize)
+ parent_class->finalize (G_OBJECT (dlg));
+}
+
+static void
+copy_theme_dialog_update_num_files (CopyThemeDialog *dlg)
+{
+ gchar *str = g_strdup_printf (_("Copying file: %u of %u"),
+ dlg->priv->index, dlg->priv->total_files);
+ gtk_progress_bar_set_text (GTK_PROGRESS_BAR (dlg->priv->progress), str);
+ g_free (str);
+}
+
+static void
+copy_theme_dialog_response (GtkDialog *dialog, gint response_id)
+{
+ g_cancellable_cancel (COPY_THEME_DIALOG (dialog)->priv->cancellable);
+}
+
+/**
+ * eel_gtk_label_make_bold.
+ *
+ * Switches the font of label to a bold equivalent.
+ * @label: The label.
+ **/
+static void
+eel_gtk_label_make_bold (GtkLabel *label)
+{
+ PangoFontDescription *font_desc;
+
+ font_desc = pango_font_description_new ();
+
+ pango_font_description_set_weight (font_desc,
+ PANGO_WEIGHT_BOLD);
+
+ /* This will only affect the weight of the font, the rest is
+ * from the current state of the widget, which comes from the
+ * theme or user prefs, since the font desc only has the
+ * weight flag turned on.
+ */
+ gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
+
+ pango_font_description_free (font_desc);
+}
+
+/* from caja */
+static void
+create_titled_label (GtkTable *table,
+ int row,
+ GtkWidget **title_widget,
+ GtkWidget **label_text_widget)
+{
+ *title_widget = gtk_label_new ("");
+ eel_gtk_label_make_bold (GTK_LABEL (*title_widget));
+ gtk_misc_set_alignment (GTK_MISC (*title_widget), 1, 0);
+ gtk_table_attach (table, *title_widget,
+ 0, 1,
+ row, row + 1,
+ GTK_FILL, 0,
+ 0, 0);
+ gtk_widget_show (*title_widget);
+
+ *label_text_widget = gtk_label_new ("");
+ gtk_label_set_ellipsize (GTK_LABEL (*label_text_widget), PANGO_ELLIPSIZE_END);
+ gtk_table_attach (table, *label_text_widget,
+ 1, 2,
+ row, row + 1,
+ GTK_FILL | GTK_EXPAND, 0,
+ 0, 0);
+ gtk_widget_show (*label_text_widget);
+ gtk_misc_set_alignment (GTK_MISC (*label_text_widget), 0, 0);
+}
diff --git a/src/copy-theme-dialog.h b/src/copy-theme-dialog.h
new file mode 100644
index 0000000..6e72017
--- /dev/null
+++ b/src/copy-theme-dialog.h
@@ -0,0 +1,56 @@
+/* copy-theme-dialog.h
+ * Copyright (C) 2008 John Millikin <[email protected]>
+ *
+ * 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, 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 __COPY_THEME_DIALOG_H__
+#define __COPY_THEME_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define COPY_THEME_DIALOG_TYPE copy_theme_dialog_get_type ()
+#define COPY_THEME_DIALOG(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, COPY_THEME_DIALOG_TYPE, CopyThemeDialog)
+#define COPY_THEME_DIALOG_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, COPY_THEME_DIALOG_TYPE, CopyThemeDialogClass)
+#define IS_COPY_THEME_DIALOG(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, COPY_THEME_DIALOG_TYPE)
+
+typedef struct _CopyThemeDialog CopyThemeDialog;
+typedef struct _CopyThemeDialogClass CopyThemeDialogClass;
+typedef struct _CopyThemeDialogPrivate CopyThemeDialogPrivate;
+
+struct _CopyThemeDialog
+{
+ GtkDialog dialog;
+ CopyThemeDialogPrivate *priv;
+};
+
+struct _CopyThemeDialogClass
+{
+ GtkDialogClass parent_class;
+
+ void (*cancelled) (CopyThemeDialog *dialog);
+ void (*complete) (CopyThemeDialog *dialog);
+};
+
+GType copy_theme_dialog_get_type (void);
+GtkWidget *copy_theme_dialog_new (GList *files);
+void copy_theme_dialog_begin (CopyThemeDialog *dialog);
+
+G_END_DECLS
+
+#endif /* __COPY_THEME_DIALOG_H__ */
diff --git a/src/copy-theme-dialog.h.orig b/src/copy-theme-dialog.h.orig
new file mode 100644
index 0000000..b1e66fd
--- /dev/null
+++ b/src/copy-theme-dialog.h.orig
@@ -0,0 +1,56 @@
+/* copy-theme-dialog.h
+ * Copyright (C) 2008 John Millikin <[email protected]>
+ *
+ * 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, 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 __COPY_THEME_DIALOG_H__
+#define __COPY_THEME_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define COPY_THEME_DIALOG_TYPE copy_theme_dialog_get_type ()
+#define COPY_THEME_DIALOG(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, COPY_THEME_DIALOG_TYPE, CopyThemeDialog)
+#define COPY_THEME_DIALOG_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, COPY_THEME_DIALOG_TYPE, CopyThemeDialogClass)
+#define IS_COPY_THEME_DIALOG(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, COPY_THEME_DIALOG_TYPE)
+
+typedef struct _CopyThemeDialog CopyThemeDialog;
+typedef struct _CopyThemeDialogClass CopyThemeDialogClass;
+typedef struct _CopyThemeDialogPrivate CopyThemeDialogPrivate;
+
+struct _CopyThemeDialog
+{
+ GtkDialog dialog;
+ CopyThemeDialogPrivate *priv;
+};
+
+struct _CopyThemeDialogClass
+{
+ GtkDialogClass parent_class;
+
+ void (*cancelled) (CopyThemeDialog *dialog);
+ void (*complete) (CopyThemeDialog *dialog);
+};
+
+GType copy_theme_dialog_get_type (void);
+GtkWidget *copy_theme_dialog_new (GList *files);
+void copy_theme_dialog_begin (CopyThemeDialog *dialog);
+
+G_END_DECLS
+
+#endif /* __COPY_THEME_DIALOG_H__ */
diff --git a/src/debug-screensaver.sh b/src/debug-screensaver.sh
new file mode 100755
index 0000000..c8b1a93
--- /dev/null
+++ b/src/debug-screensaver.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+
+# This is probably linux only at the moment
+
+if [ -z "${DBUS_SESSION_BUS_ADDRESS}" ]; then
+ pid=`pgrep -u $USER "mate-session|x-session-manager"`
+ if [ "x$pid" != "x" ]; then
+ env_address=`(cat /proc/$pid/environ; echo) | tr "\000" "\n" | grep '^DBUS_SESSION_BUS_ADDRESS='`
+ env_display=`(cat /proc/$pid/environ; echo) | tr "\000" "\n" | grep '^DISPLAY='`
+ env_xdg_cookie=`(cat /proc/$pid/environ; echo) | tr "\000" "\n" | grep '^XDG_SESSION_COOKIE='`
+ env_path=`(cat /proc/$pid/environ; echo) | tr "\000" "\n" | grep '^PATH='`
+ if [ "x$env_address" != "x" ]; then
+ echo "Setting $env_address"
+ echo "Setting $env_display"
+ echo "Setting $env_path"
+ echo "Setting $env_xdg_cookie"
+ eval "export $env_address"
+ eval "export $env_display"
+ eval "export $env_path"
+ eval "export $env_xdg_cookie"
+ fi
+ fi
+fi
+
+if [ -z "${DBUS_SESSION_BUS_ADDRESS}" ]; then
+ echo "Could not determine DBUS_SESSION_BUS_ADDRESS"
+ exit 1
+fi
+
+export G_DEBUG=fatal_criticals
+
+# kill the existing daemon
+mate-screensaver-command --exit
+
+# run the daemon in the debugger
+#gdb --args mate-screensaver --no-daemon --debug --sync
+
+# or if that isn't helpful just get the debug output
+#mate-screensaver --no-daemon --debug > /tmp/gs-debug-log.txt 2>&1
+
+# or just run it with debugging on
+mate-screensaver --no-daemon --debug
diff --git a/src/gs-auth-helper.c b/src/gs-auth-helper.c
new file mode 100644
index 0000000..ea9aefe
--- /dev/null
+++ b/src/gs-auth-helper.c
@@ -0,0 +1,213 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * written by Olaf Kirch <[email protected]>
+ * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* The idea here is to be able to run mate-screensaver-dialog without any setuid bits.
+ * Password verification happens through an external program that you feed
+ * your password to on stdin. The external command is invoked with a user
+ * name argument.
+ *
+ * The external helper does whatever authentication is necessary. Currently,
+ * SuSE uses "unix2_chkpwd", which is a variation of "unix_chkpwd" from the
+ * PAM distribution.
+ *
+ * Normally, the password helper should just authenticate the calling user
+ * (i.e. based on the caller's real uid). This is in order to prevent
+ * brute-forcing passwords in a shadow environment. A less restrictive
+ * approach would be to allow verifying other passwords as well, but always
+ * with a 2 second delay or so. (Not sure what SuSE's "unix2_chkpwd"
+ * currently does.)
+ * -- Olaf Kirch <[email protected]>, 16-Dec-2003
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gs-auth.h"
+#include "subprocs.h"
+
+static gboolean verbose_enabled = FALSE;
+
+GQuark
+gs_auth_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (! quark)
+ {
+ quark = g_quark_from_static_string ("gs_auth_error");
+ }
+
+ return quark;
+}
+
+void
+gs_auth_set_verbose (gboolean enabled)
+{
+ verbose_enabled = enabled;
+}
+
+gboolean
+gs_auth_get_verbose (void)
+{
+ return verbose_enabled;
+}
+
+static gboolean
+ext_run (const char *user,
+ const char *typed_passwd,
+ gboolean verbose)
+{
+ int pfd[2], status;
+ pid_t pid;
+
+ if (pipe (pfd) < 0)
+ {
+ return 0;
+ }
+
+ if (verbose)
+ {
+ g_message ("ext_run (%s, %s)",
+ PASSWD_HELPER_PROGRAM, user);
+ }
+
+ block_sigchld ();
+
+ if ((pid = fork ()) < 0)
+ {
+ close (pfd [0]);
+ close (pfd [1]);
+ return FALSE;
+ }
+
+ if (pid == 0)
+ {
+ close (pfd [1]);
+ if (pfd [0] != 0)
+ {
+ dup2 (pfd [0], 0);
+ }
+
+ /* Helper is invoked as helper service-name [user] */
+ execlp (PASSWD_HELPER_PROGRAM, PASSWD_HELPER_PROGRAM, "mate-screensaver", user, NULL);
+ if (verbose)
+ {
+ g_message ("%s: %s", PASSWD_HELPER_PROGRAM, g_strerror (errno));
+ }
+
+ exit (1);
+ }
+
+ close (pfd [0]);
+
+ /* Write out password to helper process */
+ if (!typed_passwd)
+ {
+ typed_passwd = "";
+ }
+ write (pfd [1], typed_passwd, strlen (typed_passwd));
+ close (pfd [1]);
+
+ while (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ {
+ continue;
+ }
+
+ if (verbose)
+ {
+ g_message ("ext_run: waitpid failed: %s\n",
+ g_strerror (errno));
+ }
+
+ unblock_sigchld ();
+ return FALSE;
+ }
+
+ unblock_sigchld ();
+
+ if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error)
+{
+ gboolean res = FALSE;
+ char *password;
+
+ password = NULL;
+
+ /* ask for the password for user */
+ if (func != NULL)
+ {
+ func (GS_AUTH_MESSAGE_PROMPT_ECHO_OFF,
+ "Password: ",
+ &password,
+ data);
+ }
+
+ if (password == NULL)
+ {
+ return FALSE;
+ }
+
+ res = ext_run (username, password, gs_auth_get_verbose ());
+
+ return res;
+}
+
+gboolean
+gs_auth_init (void)
+{
+ return TRUE;
+}
+
+gboolean
+gs_auth_priv_init (void)
+{
+ /* Make sure the passwd helper exists */
+ if (g_access (PASSWD_HELPER_PROGRAM, X_OK) < 0)
+ {
+ g_warning ("%s does not exist. "
+ "password authentication via "
+ "external helper will not work.",
+ PASSWD_HELPER_PROGRAM);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/gs-auth-helper.c.orig b/src/gs-auth-helper.c.orig
new file mode 100644
index 0000000..946aef3
--- /dev/null
+++ b/src/gs-auth-helper.c.orig
@@ -0,0 +1,213 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * written by Olaf Kirch <[email protected]>
+ * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+/* The idea here is to be able to run mate-screensaver-dialog without any setuid bits.
+ * Password verification happens through an external program that you feed
+ * your password to on stdin. The external command is invoked with a user
+ * name argument.
+ *
+ * The external helper does whatever authentication is necessary. Currently,
+ * SuSE uses "unix2_chkpwd", which is a variation of "unix_chkpwd" from the
+ * PAM distribution.
+ *
+ * Normally, the password helper should just authenticate the calling user
+ * (i.e. based on the caller's real uid). This is in order to prevent
+ * brute-forcing passwords in a shadow environment. A less restrictive
+ * approach would be to allow verifying other passwords as well, but always
+ * with a 2 second delay or so. (Not sure what SuSE's "unix2_chkpwd"
+ * currently does.)
+ * -- Olaf Kirch <[email protected]>, 16-Dec-2003
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gs-auth.h"
+#include "subprocs.h"
+
+static gboolean verbose_enabled = FALSE;
+
+GQuark
+gs_auth_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (! quark)
+ {
+ quark = g_quark_from_static_string ("gs_auth_error");
+ }
+
+ return quark;
+}
+
+void
+gs_auth_set_verbose (gboolean enabled)
+{
+ verbose_enabled = enabled;
+}
+
+gboolean
+gs_auth_get_verbose (void)
+{
+ return verbose_enabled;
+}
+
+static gboolean
+ext_run (const char *user,
+ const char *typed_passwd,
+ gboolean verbose)
+{
+ int pfd[2], status;
+ pid_t pid;
+
+ if (pipe (pfd) < 0)
+ {
+ return 0;
+ }
+
+ if (verbose)
+ {
+ g_message ("ext_run (%s, %s)",
+ PASSWD_HELPER_PROGRAM, user);
+ }
+
+ block_sigchld ();
+
+ if ((pid = fork ()) < 0)
+ {
+ close (pfd [0]);
+ close (pfd [1]);
+ return FALSE;
+ }
+
+ if (pid == 0)
+ {
+ close (pfd [1]);
+ if (pfd [0] != 0)
+ {
+ dup2 (pfd [0], 0);
+ }
+
+ /* Helper is invoked as helper service-name [user] */
+ execlp (PASSWD_HELPER_PROGRAM, PASSWD_HELPER_PROGRAM, "mate-screensaver", user, NULL);
+ if (verbose)
+ {
+ g_message ("%s: %s", PASSWD_HELPER_PROGRAM, g_strerror (errno));
+ }
+
+ exit (1);
+ }
+
+ close (pfd [0]);
+
+ /* Write out password to helper process */
+ if (!typed_passwd)
+ {
+ typed_passwd = "";
+ }
+ write (pfd [1], typed_passwd, strlen (typed_passwd));
+ close (pfd [1]);
+
+ while (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ {
+ continue;
+ }
+
+ if (verbose)
+ {
+ g_message ("ext_run: waitpid failed: %s\n",
+ g_strerror (errno));
+ }
+
+ unblock_sigchld ();
+ return FALSE;
+ }
+
+ unblock_sigchld ();
+
+ if (! WIFEXITED (status) || WEXITSTATUS (status) != 0)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error)
+{
+ gboolean res = FALSE;
+ char *password;
+
+ password = NULL;
+
+ /* ask for the password for user */
+ if (func != NULL)
+ {
+ func (GS_AUTH_MESSAGE_PROMPT_ECHO_OFF,
+ "Password: ",
+ &password,
+ data);
+ }
+
+ if (password == NULL)
+ {
+ return FALSE;
+ }
+
+ res = ext_run (username, password, gs_auth_get_verbose ());
+
+ return res;
+}
+
+gboolean
+gs_auth_init (void)
+{
+ return TRUE;
+}
+
+gboolean
+gs_auth_priv_init (void)
+{
+ /* Make sure the passwd helper exists */
+ if (g_access (PASSWD_HELPER_PROGRAM, X_OK) < 0)
+ {
+ g_warning ("%s does not exist. "
+ "password authentication via "
+ "external helper will not work.",
+ PASSWD_HELPER_PROGRAM);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/gs-auth-pam.c b/src/gs-auth-pam.c
new file mode 100644
index 0000000..44c8aca
--- /dev/null
+++ b/src/gs-auth-pam.c
@@ -0,0 +1,843 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ * Copyright (C) 2006 Ray Strode <[email protected]>
+ * Copyright (C) 2003 Bill Nottingham <[email protected]>
+ * Copyright (c) 1993-2003 Jamie Zawinski <[email protected]>
+ *
+ * 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>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <security/pam_appl.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-auth.h"
+
+#include "subprocs.h"
+
+/* Some time between Red Hat 4.2 and 7.0, the words were transposed
+ in the various PAM_x_CRED macro names. Yay!
+*/
+#ifndef PAM_REFRESH_CRED
+# define PAM_REFRESH_CRED PAM_CRED_REFRESH
+#endif
+
+#ifdef HAVE_PAM_FAIL_DELAY
+/* We handle delays ourself.*/
+/* Don't set this to 0 (Linux bug workaround.) */
+# define PAM_NO_DELAY(pamh) pam_fail_delay ((pamh), 1)
+#else /* !HAVE_PAM_FAIL_DELAY */
+# define PAM_NO_DELAY(pamh) /* */
+#endif /* !HAVE_PAM_FAIL_DELAY */
+
+
+/* On SunOS 5.6, and on Linux with PAM 0.64, pam_strerror() takes two args.
+ On some other Linux systems with some other version of PAM (e.g.,
+ whichever Debian release comes with a 2.2.5 kernel) it takes one arg.
+ I can't tell which is more "recent" or "correct" behavior, so configure
+ figures out which is in use for us. Shoot me!
+*/
+#ifdef PAM_STRERROR_TWO_ARGS
+# define PAM_STRERROR(pamh, status) pam_strerror((pamh), (status))
+#else /* !PAM_STRERROR_TWO_ARGS */
+# define PAM_STRERROR(pamh, status) pam_strerror((status))
+#endif /* !PAM_STRERROR_TWO_ARGS */
+
+static gboolean verbose_enabled = FALSE;
+static pam_handle_t *pam_handle = NULL;
+static gboolean did_we_ask_for_password = FALSE;
+
+struct pam_closure
+{
+ const char *username;
+ GSAuthMessageFunc cb_func;
+ gpointer cb_data;
+ int signal_fd;
+ int result;
+};
+
+typedef struct
+{
+ struct pam_closure *closure;
+ GSAuthMessageStyle style;
+ const char *msg;
+ char **resp;
+ gboolean should_interrupt_stack;
+} GsAuthMessageHandlerData;
+
+static GCond *message_handled_condition;
+static GMutex *message_handler_mutex;
+
+GQuark
+gs_auth_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (! quark)
+ {
+ quark = g_quark_from_static_string ("gs_auth_error");
+ }
+
+ return quark;
+}
+
+void
+gs_auth_set_verbose (gboolean enabled)
+{
+ verbose_enabled = enabled;
+}
+
+gboolean
+gs_auth_get_verbose (void)
+{
+ return verbose_enabled;
+}
+
+static GSAuthMessageStyle
+pam_style_to_gs_style (int pam_style)
+{
+ GSAuthMessageStyle style;
+
+ switch (pam_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ style = GS_AUTH_MESSAGE_PROMPT_ECHO_ON;
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ style = GS_AUTH_MESSAGE_PROMPT_ECHO_OFF;
+ break;
+ case PAM_ERROR_MSG:
+ style = GS_AUTH_MESSAGE_ERROR_MSG;
+ break;
+ case PAM_TEXT_INFO:
+ style = GS_AUTH_MESSAGE_TEXT_INFO;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return style;
+}
+
+static gboolean
+auth_message_handler (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data)
+{
+ gboolean ret;
+
+ ret = TRUE;
+ *response = NULL;
+
+ switch (style)
+ {
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
+ break;
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
+ if (msg != NULL && g_str_has_prefix (msg, _("Password:")))
+ {
+ did_we_ask_for_password = TRUE;
+ }
+ break;
+ case GS_AUTH_MESSAGE_ERROR_MSG:
+ break;
+ case GS_AUTH_MESSAGE_TEXT_INFO:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return ret;
+}
+
+static gboolean
+gs_auth_queued_message_handler (GsAuthMessageHandlerData *data)
+{
+ gboolean res;
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Waiting for lock");
+ }
+
+ g_mutex_lock (message_handler_mutex);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Waiting for response");
+ }
+
+ res = data->closure->cb_func (data->style,
+ data->msg,
+ data->resp,
+ data->closure->cb_data);
+
+ data->should_interrupt_stack = res == FALSE;
+
+ g_cond_signal (message_handled_condition);
+ g_mutex_unlock (message_handler_mutex);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Got response");
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gs_auth_run_message_handler (struct pam_closure *c,
+ GSAuthMessageStyle style,
+ const char *msg,
+ char **resp)
+{
+ GsAuthMessageHandlerData data;
+
+ data.closure = c;
+ data.style = style;
+ data.msg = msg;
+ data.resp = resp;
+ data.should_interrupt_stack = TRUE;
+
+ g_mutex_lock (message_handler_mutex);
+
+ /* Queue the callback in the gui (the main) thread
+ */
+ g_idle_add ((GSourceFunc) gs_auth_queued_message_handler, &data);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Waiting for respose to message style %d: '%s'", style, msg);
+ }
+
+ /* Wait for the response
+ */
+ g_cond_wait (message_handled_condition,
+ message_handler_mutex);
+ g_mutex_unlock (message_handler_mutex);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Got respose to message style %d: interrupt:%d", style, data.should_interrupt_stack);
+ }
+
+ return data.should_interrupt_stack == FALSE;
+}
+
+static int
+pam_conversation (int nmsgs,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *closure)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ struct pam_closure *c = (struct pam_closure *) closure;
+ gboolean res;
+ int ret;
+
+ reply = (struct pam_response *) calloc (nmsgs, sizeof (*reply));
+
+ if (reply == NULL)
+ {
+ return PAM_CONV_ERR;
+ }
+
+ res = TRUE;
+ ret = PAM_SUCCESS;
+
+ for (replies = 0; replies < nmsgs && ret == PAM_SUCCESS; replies++)
+ {
+ GSAuthMessageStyle style;
+ char *utf8_msg;
+
+ style = pam_style_to_gs_style (msg [replies]->msg_style);
+
+ utf8_msg = g_locale_to_utf8 (msg [replies]->msg,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+
+ /* if we couldn't convert text from locale then
+ * assume utf-8 and hope for the best */
+ if (utf8_msg == NULL)
+ {
+ char *p;
+ char *q;
+
+ utf8_msg = g_strdup (msg [replies]->msg);
+
+ p = utf8_msg;
+ while (*p != '\0' && !g_utf8_validate ((const char *)p, -1, (const char **)&q))
+ {
+ *q = '?';
+ p = q + 1;
+ }
+ }
+
+ /* handle message locally first */
+ auth_message_handler (style,
+ utf8_msg,
+ &reply [replies].resp,
+ NULL);
+
+ if (c->cb_func != NULL)
+ {
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Handling message style %d: '%s'", style, utf8_msg);
+ }
+
+ /* blocks until the gui responds
+ */
+ res = gs_auth_run_message_handler (c,
+ style,
+ utf8_msg,
+ &reply [replies].resp);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Msg handler returned %d", res);
+ }
+
+ /* If the handler returns FALSE - interrupt the PAM stack */
+ if (res)
+ {
+ reply [replies].resp_retcode = PAM_SUCCESS;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i <= replies; i++)
+ {
+ free (reply [i].resp);
+ }
+ free (reply);
+ reply = NULL;
+ ret = PAM_CONV_ERR;
+ }
+ }
+
+ g_free (utf8_msg);
+ }
+
+ *resp = reply;
+
+ return ret;
+}
+
+static gboolean
+close_pam_handle (int status)
+{
+
+ if (pam_handle != NULL)
+ {
+ int status2;
+
+ status2 = pam_end (pam_handle, status);
+ pam_handle = NULL;
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message (" pam_end (...) ==> %d (%s)",
+ status2,
+ (status2 == PAM_SUCCESS ? "Success" : "Failure"));
+ }
+ }
+
+ if (message_handled_condition != NULL)
+ {
+ g_cond_free (message_handled_condition);
+ message_handled_condition = NULL;
+ }
+
+ if (message_handler_mutex != NULL)
+ {
+ g_mutex_free (message_handler_mutex);
+ message_handler_mutex = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+create_pam_handle (const char *username,
+ const char *display,
+ struct pam_conv *conv,
+ int *status_code)
+{
+ int status;
+ const char *service = PAM_SERVICE_NAME;
+ char *disp;
+ gboolean ret;
+
+ if (pam_handle != NULL)
+ {
+ g_warning ("create_pam_handle: Stale pam handle around, cleaning up");
+ close_pam_handle (PAM_SUCCESS);
+ }
+
+ /* init things */
+ pam_handle = NULL;
+ status = -1;
+ disp = NULL;
+ ret = TRUE;
+
+ /* Initialize a PAM session for the user */
+ if ((status = pam_start (service, username, conv, &pam_handle)) != PAM_SUCCESS)
+ {
+ pam_handle = NULL;
+ g_warning (_("Unable to establish service %s: %s\n"),
+ service,
+ PAM_STRERROR (NULL, status));
+
+ if (status_code != NULL)
+ {
+ *status_code = status;
+ }
+
+ ret = FALSE;
+ goto out;
+ }
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("pam_start (\"%s\", \"%s\", ...) ==> %d (%s)",
+ service,
+ username,
+ status,
+ PAM_STRERROR (pam_handle, status));
+ }
+
+ disp = g_strdup (display);
+ if (disp == NULL)
+ {
+ disp = g_strdup (":0.0");
+ }
+
+ if ((status = pam_set_item (pam_handle, PAM_TTY, disp)) != PAM_SUCCESS)
+ {
+ g_warning (_("Can't set PAM_TTY=%s"), display);
+
+ if (status_code != NULL)
+ {
+ *status_code = status;
+ }
+
+ ret = FALSE;
+ goto out;
+ }
+
+ ret = TRUE;
+ message_handled_condition = g_cond_new ();
+ message_handler_mutex = g_mutex_new ();
+
+out:
+ if (status_code != NULL)
+ {
+ *status_code = status;
+ }
+
+ g_free (disp);
+
+ return ret;
+}
+
+static void
+set_pam_error (GError **error,
+ int status)
+{
+ if (status == PAM_AUTH_ERR || status == PAM_USER_UNKNOWN)
+ {
+ char *msg;
+
+ if (did_we_ask_for_password)
+ {
+ msg = g_strdup (_("Incorrect password."));
+ }
+ else
+ {
+ msg = g_strdup (_("Authentication failed."));
+ }
+
+ g_set_error (error,
+ GS_AUTH_ERROR,
+ GS_AUTH_ERROR_AUTH_ERROR,
+ "%s",
+ msg);
+ g_free (msg);
+ }
+ else if (status == PAM_PERM_DENIED)
+ {
+ g_set_error (error,
+ GS_AUTH_ERROR,
+ GS_AUTH_ERROR_AUTH_DENIED,
+ "%s",
+ _("Not permitted to gain access at this time."));
+ }
+ else if (status == PAM_ACCT_EXPIRED)
+ {
+ g_set_error (error,
+ GS_AUTH_ERROR,
+ GS_AUTH_ERROR_AUTH_DENIED,
+ "%s",
+ _("No longer permitted to access the system."));
+ }
+
+}
+
+static int
+gs_auth_thread_func (int auth_operation_fd)
+{
+ static const int flags = 0;
+ int status;
+ int status2;
+ struct timespec timeout;
+ sigset_t set;
+ const void *p;
+
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 1;
+
+ set = block_sigchld ();
+
+ status = pam_authenticate (pam_handle, flags);
+
+ sigtimedwait (&set, NULL, &timeout);
+ unblock_sigchld ();
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message (" pam_authenticate (...) ==> %d (%s)",
+ status,
+ PAM_STRERROR (pam_handle, status));
+ }
+
+ if (status != PAM_SUCCESS)
+ {
+ goto done;
+ }
+
+ if ((status = pam_get_item (pam_handle, PAM_USER, &p)) != PAM_SUCCESS)
+ {
+ /* is not really an auth problem, but it will
+ pretty much look as such, it shouldn't really
+ happen */
+ goto done;
+ }
+
+ /* We don't actually care if the account modules fail or succeed,
+ * but we need to run them anyway because certain pam modules
+ * depend on side effects of the account modules getting run.
+ */
+ status2 = pam_acct_mgmt (pam_handle, 0);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("pam_acct_mgmt (...) ==> %d (%s)\n",
+ status2,
+ PAM_STRERROR (pam_handle, status2));
+ }
+
+ /* FIXME: should we handle these? */
+ switch (status2)
+ {
+ case PAM_SUCCESS:
+ break;
+ case PAM_NEW_AUTHTOK_REQD:
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ break;
+ case PAM_ACCT_EXPIRED:
+ break;
+ case PAM_PERM_DENIED:
+ break;
+ default :
+ break;
+ }
+
+ /* Each time we successfully authenticate, refresh credentials,
+ for Kerberos/AFS/DCE/etc. If this fails, just ignore that
+ failure and blunder along; it shouldn't matter.
+
+ Note: this used to be PAM_REFRESH_CRED instead of
+ PAM_REINITIALIZE_CRED, but Jason Heiss <[email protected]>
+ says that the Linux PAM library ignores that one, and only refreshes
+ credentials when using PAM_REINITIALIZE_CRED.
+ */
+ status2 = pam_setcred (pam_handle, PAM_REINITIALIZE_CRED);
+ if (gs_auth_get_verbose ())
+ {
+ g_message (" pam_setcred (...) ==> %d (%s)",
+ status2,
+ PAM_STRERROR (pam_handle, status2));
+ }
+
+done:
+ /* we're done, close the fd and wake up the main
+ * loop
+ */
+ close (auth_operation_fd);
+
+ return status;
+}
+
+static gboolean
+gs_auth_loop_quit (GIOChannel *source,
+ GIOCondition condition,
+ gboolean *thread_done)
+{
+ *thread_done = TRUE;
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+gs_auth_pam_verify_user (pam_handle_t *handle,
+ int *status)
+{
+ GThread *auth_thread;
+ GIOChannel *channel;
+ guint watch_id;
+ int auth_operation_fds[2];
+ int auth_status;
+ gboolean thread_done;
+
+ channel = NULL;
+ watch_id = 0;
+ auth_status = PAM_AUTH_ERR;
+
+ /* This pipe gives us a set of fds we can hook into
+ * the event loop to be notified when our helper thread
+ * is ready to be reaped.
+ */
+ if (pipe (auth_operation_fds) < 0)
+ {
+ goto out;
+ }
+
+ if (fcntl (auth_operation_fds[0], F_SETFD, FD_CLOEXEC) < 0)
+ {
+ close (auth_operation_fds[0]);
+ close (auth_operation_fds[1]);
+ goto out;
+ }
+
+ if (fcntl (auth_operation_fds[1], F_SETFD, FD_CLOEXEC) < 0)
+ {
+ close (auth_operation_fds[0]);
+ close (auth_operation_fds[1]);
+ goto out;
+ }
+
+ channel = g_io_channel_unix_new (auth_operation_fds[0]);
+
+ /* we use a recursive main loop to process ui events
+ * while we wait on a thread to handle the blocking parts
+ * of pam authentication.
+ */
+ thread_done = FALSE;
+ watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP,
+ (GIOFunc) gs_auth_loop_quit, &thread_done);
+
+ auth_thread = g_thread_create ((GThreadFunc) gs_auth_thread_func,
+ GINT_TO_POINTER (auth_operation_fds[1]),
+ TRUE, NULL);
+
+ if (auth_thread == NULL)
+ {
+ goto out;
+ }
+
+ gtk_main ();
+
+ /* if the event loop was quit before the thread is done then we can't
+ * reap the thread without blocking on it finishing. The
+ * thread may not ever finish though if the pam module is blocking.
+ *
+ * The only time the event loop is going to stop when the thread isn't
+ * done, however, is if the dialog quits early (from, e.g., "cancel"),
+ * so we can just exit. An alternative option would be to switch to
+ * using pthreads directly and calling pthread_cancel.
+ */
+ if (!thread_done)
+ {
+ raise (SIGTERM);
+ }
+
+ auth_status = GPOINTER_TO_INT (g_thread_join (auth_thread));
+
+out:
+ if (watch_id != 0)
+ {
+ g_source_remove (watch_id);
+ }
+
+ if (channel != NULL)
+ {
+ g_io_channel_unref (channel);
+ }
+
+ if (status)
+ {
+ *status = auth_status;
+ }
+
+ return auth_status == PAM_SUCCESS;
+}
+
+gboolean
+gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error)
+{
+ int status = -1;
+ struct pam_conv conv;
+ struct pam_closure c;
+ struct passwd *pwent;
+
+ pwent = getpwnam (username);
+ if (pwent == NULL)
+ {
+ return FALSE;
+ }
+
+ c.username = username;
+ c.cb_func = func;
+ c.cb_data = data;
+
+ conv.conv = &pam_conversation;
+ conv.appdata_ptr = (void *) &c;
+
+ /* Initialize PAM. */
+ create_pam_handle (username, display, &conv, &status);
+ if (status != PAM_SUCCESS)
+ {
+ goto done;
+ }
+
+ pam_set_item (pam_handle, PAM_USER_PROMPT, _("Username:"));
+
+ PAM_NO_DELAY(pam_handle);
+
+ did_we_ask_for_password = FALSE;
+ if (! gs_auth_pam_verify_user (pam_handle, &status))
+ {
+ goto done;
+ }
+
+done:
+ if (status != PAM_SUCCESS)
+ {
+ set_pam_error (error, status);
+ }
+
+ close_pam_handle (status);
+
+ return (status == PAM_SUCCESS ? TRUE : FALSE);
+}
+
+gboolean
+gs_auth_init (void)
+{
+ return TRUE;
+}
+
+gboolean
+gs_auth_priv_init (void)
+{
+ /* We have nothing to do at init-time.
+ However, we might as well do some error checking.
+ If "/etc/pam.d" exists and is a directory, but "/etc/pam.d/xlock"
+ does not exist, warn that PAM probably isn't going to work.
+
+ This is a priv-init instead of a non-priv init in case the directory
+ is unreadable or something (don't know if that actually happens.)
+ */
+ const char dir [] = "/etc/pam.d";
+ const char file [] = "/etc/pam.d/" PAM_SERVICE_NAME;
+ const char file2 [] = "/etc/pam.conf";
+ struct stat st;
+
+ if (g_stat (dir, &st) == 0 && st.st_mode & S_IFDIR)
+ {
+ if (g_stat (file, &st) != 0)
+ {
+ g_warning ("%s does not exist.\n"
+ "Authentication via PAM is unlikely to work.",
+ file);
+ }
+ }
+ else if (g_stat (file2, &st) == 0)
+ {
+ FILE *f = g_fopen (file2, "r");
+ if (f)
+ {
+ gboolean ok = FALSE;
+ char buf[255];
+ while (fgets (buf, sizeof(buf), f))
+ {
+ if (strstr (buf, PAM_SERVICE_NAME))
+ {
+ ok = TRUE;
+ break;
+ }
+ }
+
+ fclose (f);
+ if (!ok)
+ {
+ g_warning ("%s does not list the `%s' service.\n"
+ "Authentication via PAM is unlikely to work.",
+ file2, PAM_SERVICE_NAME);
+ }
+ }
+ /* else warn about file2 existing but being unreadable? */
+ }
+ else
+ {
+ g_warning ("Neither %s nor %s exist.\n"
+ "Authentication via PAM is unlikely to work.",
+ file2, file);
+ }
+
+ /* Return true anyway, just in case. */
+ return TRUE;
+}
diff --git a/src/gs-auth-pam.c.orig b/src/gs-auth-pam.c.orig
new file mode 100644
index 0000000..88852d9
--- /dev/null
+++ b/src/gs-auth-pam.c.orig
@@ -0,0 +1,843 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ * Copyright (C) 2006 Ray Strode <[email protected]>
+ * Copyright (C) 2003 Bill Nottingham <[email protected]>
+ * Copyright (c) 1993-2003 Jamie Zawinski <[email protected]>
+ *
+ * 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>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <security/pam_appl.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-auth.h"
+
+#include "subprocs.h"
+
+/* Some time between Red Hat 4.2 and 7.0, the words were transposed
+ in the various PAM_x_CRED macro names. Yay!
+*/
+#ifndef PAM_REFRESH_CRED
+# define PAM_REFRESH_CRED PAM_CRED_REFRESH
+#endif
+
+#ifdef HAVE_PAM_FAIL_DELAY
+/* We handle delays ourself.*/
+/* Don't set this to 0 (Linux bug workaround.) */
+# define PAM_NO_DELAY(pamh) pam_fail_delay ((pamh), 1)
+#else /* !HAVE_PAM_FAIL_DELAY */
+# define PAM_NO_DELAY(pamh) /* */
+#endif /* !HAVE_PAM_FAIL_DELAY */
+
+
+/* On SunOS 5.6, and on Linux with PAM 0.64, pam_strerror() takes two args.
+ On some other Linux systems with some other version of PAM (e.g.,
+ whichever Debian release comes with a 2.2.5 kernel) it takes one arg.
+ I can't tell which is more "recent" or "correct" behavior, so configure
+ figures out which is in use for us. Shoot me!
+*/
+#ifdef PAM_STRERROR_TWO_ARGS
+# define PAM_STRERROR(pamh, status) pam_strerror((pamh), (status))
+#else /* !PAM_STRERROR_TWO_ARGS */
+# define PAM_STRERROR(pamh, status) pam_strerror((status))
+#endif /* !PAM_STRERROR_TWO_ARGS */
+
+static gboolean verbose_enabled = FALSE;
+static pam_handle_t *pam_handle = NULL;
+static gboolean did_we_ask_for_password = FALSE;
+
+struct pam_closure
+{
+ const char *username;
+ GSAuthMessageFunc cb_func;
+ gpointer cb_data;
+ int signal_fd;
+ int result;
+};
+
+typedef struct
+{
+ struct pam_closure *closure;
+ GSAuthMessageStyle style;
+ const char *msg;
+ char **resp;
+ gboolean should_interrupt_stack;
+} GsAuthMessageHandlerData;
+
+static GCond *message_handled_condition;
+static GMutex *message_handler_mutex;
+
+GQuark
+gs_auth_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (! quark)
+ {
+ quark = g_quark_from_static_string ("gs_auth_error");
+ }
+
+ return quark;
+}
+
+void
+gs_auth_set_verbose (gboolean enabled)
+{
+ verbose_enabled = enabled;
+}
+
+gboolean
+gs_auth_get_verbose (void)
+{
+ return verbose_enabled;
+}
+
+static GSAuthMessageStyle
+pam_style_to_gs_style (int pam_style)
+{
+ GSAuthMessageStyle style;
+
+ switch (pam_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ style = GS_AUTH_MESSAGE_PROMPT_ECHO_ON;
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ style = GS_AUTH_MESSAGE_PROMPT_ECHO_OFF;
+ break;
+ case PAM_ERROR_MSG:
+ style = GS_AUTH_MESSAGE_ERROR_MSG;
+ break;
+ case PAM_TEXT_INFO:
+ style = GS_AUTH_MESSAGE_TEXT_INFO;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return style;
+}
+
+static gboolean
+auth_message_handler (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data)
+{
+ gboolean ret;
+
+ ret = TRUE;
+ *response = NULL;
+
+ switch (style)
+ {
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
+ break;
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
+ if (msg != NULL && g_str_has_prefix (msg, _("Password:")))
+ {
+ did_we_ask_for_password = TRUE;
+ }
+ break;
+ case GS_AUTH_MESSAGE_ERROR_MSG:
+ break;
+ case GS_AUTH_MESSAGE_TEXT_INFO:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return ret;
+}
+
+static gboolean
+gs_auth_queued_message_handler (GsAuthMessageHandlerData *data)
+{
+ gboolean res;
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Waiting for lock");
+ }
+
+ g_mutex_lock (message_handler_mutex);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Waiting for response");
+ }
+
+ res = data->closure->cb_func (data->style,
+ data->msg,
+ data->resp,
+ data->closure->cb_data);
+
+ data->should_interrupt_stack = res == FALSE;
+
+ g_cond_signal (message_handled_condition);
+ g_mutex_unlock (message_handler_mutex);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Got response");
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gs_auth_run_message_handler (struct pam_closure *c,
+ GSAuthMessageStyle style,
+ const char *msg,
+ char **resp)
+{
+ GsAuthMessageHandlerData data;
+
+ data.closure = c;
+ data.style = style;
+ data.msg = msg;
+ data.resp = resp;
+ data.should_interrupt_stack = TRUE;
+
+ g_mutex_lock (message_handler_mutex);
+
+ /* Queue the callback in the gui (the main) thread
+ */
+ g_idle_add ((GSourceFunc) gs_auth_queued_message_handler, &data);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Waiting for respose to message style %d: '%s'", style, msg);
+ }
+
+ /* Wait for the response
+ */
+ g_cond_wait (message_handled_condition,
+ message_handler_mutex);
+ g_mutex_unlock (message_handler_mutex);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Got respose to message style %d: interrupt:%d", style, data.should_interrupt_stack);
+ }
+
+ return data.should_interrupt_stack == FALSE;
+}
+
+static int
+pam_conversation (int nmsgs,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *closure)
+{
+ int replies = 0;
+ struct pam_response *reply = NULL;
+ struct pam_closure *c = (struct pam_closure *) closure;
+ gboolean res;
+ int ret;
+
+ reply = (struct pam_response *) calloc (nmsgs, sizeof (*reply));
+
+ if (reply == NULL)
+ {
+ return PAM_CONV_ERR;
+ }
+
+ res = TRUE;
+ ret = PAM_SUCCESS;
+
+ for (replies = 0; replies < nmsgs && ret == PAM_SUCCESS; replies++)
+ {
+ GSAuthMessageStyle style;
+ char *utf8_msg;
+
+ style = pam_style_to_gs_style (msg [replies]->msg_style);
+
+ utf8_msg = g_locale_to_utf8 (msg [replies]->msg,
+ -1,
+ NULL,
+ NULL,
+ NULL);
+
+ /* if we couldn't convert text from locale then
+ * assume utf-8 and hope for the best */
+ if (utf8_msg == NULL)
+ {
+ char *p;
+ char *q;
+
+ utf8_msg = g_strdup (msg [replies]->msg);
+
+ p = utf8_msg;
+ while (*p != '\0' && !g_utf8_validate ((const char *)p, -1, (const char **)&q))
+ {
+ *q = '?';
+ p = q + 1;
+ }
+ }
+
+ /* handle message locally first */
+ auth_message_handler (style,
+ utf8_msg,
+ &reply [replies].resp,
+ NULL);
+
+ if (c->cb_func != NULL)
+ {
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Handling message style %d: '%s'", style, utf8_msg);
+ }
+
+ /* blocks until the gui responds
+ */
+ res = gs_auth_run_message_handler (c,
+ style,
+ utf8_msg,
+ &reply [replies].resp);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("Msg handler returned %d", res);
+ }
+
+ /* If the handler returns FALSE - interrupt the PAM stack */
+ if (res)
+ {
+ reply [replies].resp_retcode = PAM_SUCCESS;
+ }
+ else
+ {
+ int i;
+ for (i = 0; i <= replies; i++)
+ {
+ free (reply [i].resp);
+ }
+ free (reply);
+ reply = NULL;
+ ret = PAM_CONV_ERR;
+ }
+ }
+
+ g_free (utf8_msg);
+ }
+
+ *resp = reply;
+
+ return ret;
+}
+
+static gboolean
+close_pam_handle (int status)
+{
+
+ if (pam_handle != NULL)
+ {
+ int status2;
+
+ status2 = pam_end (pam_handle, status);
+ pam_handle = NULL;
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message (" pam_end (...) ==> %d (%s)",
+ status2,
+ (status2 == PAM_SUCCESS ? "Success" : "Failure"));
+ }
+ }
+
+ if (message_handled_condition != NULL)
+ {
+ g_cond_free (message_handled_condition);
+ message_handled_condition = NULL;
+ }
+
+ if (message_handler_mutex != NULL)
+ {
+ g_mutex_free (message_handler_mutex);
+ message_handler_mutex = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+create_pam_handle (const char *username,
+ const char *display,
+ struct pam_conv *conv,
+ int *status_code)
+{
+ int status;
+ const char *service = PAM_SERVICE_NAME;
+ char *disp;
+ gboolean ret;
+
+ if (pam_handle != NULL)
+ {
+ g_warning ("create_pam_handle: Stale pam handle around, cleaning up");
+ close_pam_handle (PAM_SUCCESS);
+ }
+
+ /* init things */
+ pam_handle = NULL;
+ status = -1;
+ disp = NULL;
+ ret = TRUE;
+
+ /* Initialize a PAM session for the user */
+ if ((status = pam_start (service, username, conv, &pam_handle)) != PAM_SUCCESS)
+ {
+ pam_handle = NULL;
+ g_warning (_("Unable to establish service %s: %s\n"),
+ service,
+ PAM_STRERROR (NULL, status));
+
+ if (status_code != NULL)
+ {
+ *status_code = status;
+ }
+
+ ret = FALSE;
+ goto out;
+ }
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("pam_start (\"%s\", \"%s\", ...) ==> %d (%s)",
+ service,
+ username,
+ status,
+ PAM_STRERROR (pam_handle, status));
+ }
+
+ disp = g_strdup (display);
+ if (disp == NULL)
+ {
+ disp = g_strdup (":0.0");
+ }
+
+ if ((status = pam_set_item (pam_handle, PAM_TTY, disp)) != PAM_SUCCESS)
+ {
+ g_warning (_("Can't set PAM_TTY=%s"), display);
+
+ if (status_code != NULL)
+ {
+ *status_code = status;
+ }
+
+ ret = FALSE;
+ goto out;
+ }
+
+ ret = TRUE;
+ message_handled_condition = g_cond_new ();
+ message_handler_mutex = g_mutex_new ();
+
+out:
+ if (status_code != NULL)
+ {
+ *status_code = status;
+ }
+
+ g_free (disp);
+
+ return ret;
+}
+
+static void
+set_pam_error (GError **error,
+ int status)
+{
+ if (status == PAM_AUTH_ERR || status == PAM_USER_UNKNOWN)
+ {
+ char *msg;
+
+ if (did_we_ask_for_password)
+ {
+ msg = g_strdup (_("Incorrect password."));
+ }
+ else
+ {
+ msg = g_strdup (_("Authentication failed."));
+ }
+
+ g_set_error (error,
+ GS_AUTH_ERROR,
+ GS_AUTH_ERROR_AUTH_ERROR,
+ "%s",
+ msg);
+ g_free (msg);
+ }
+ else if (status == PAM_PERM_DENIED)
+ {
+ g_set_error (error,
+ GS_AUTH_ERROR,
+ GS_AUTH_ERROR_AUTH_DENIED,
+ "%s",
+ _("Not permitted to gain access at this time."));
+ }
+ else if (status == PAM_ACCT_EXPIRED)
+ {
+ g_set_error (error,
+ GS_AUTH_ERROR,
+ GS_AUTH_ERROR_AUTH_DENIED,
+ "%s",
+ _("No longer permitted to access the system."));
+ }
+
+}
+
+static int
+gs_auth_thread_func (int auth_operation_fd)
+{
+ static const int flags = 0;
+ int status;
+ int status2;
+ struct timespec timeout;
+ sigset_t set;
+ const void *p;
+
+ timeout.tv_sec = 0;
+ timeout.tv_nsec = 1;
+
+ set = block_sigchld ();
+
+ status = pam_authenticate (pam_handle, flags);
+
+ sigtimedwait (&set, NULL, &timeout);
+ unblock_sigchld ();
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message (" pam_authenticate (...) ==> %d (%s)",
+ status,
+ PAM_STRERROR (pam_handle, status));
+ }
+
+ if (status != PAM_SUCCESS)
+ {
+ goto done;
+ }
+
+ if ((status = pam_get_item (pam_handle, PAM_USER, &p)) != PAM_SUCCESS)
+ {
+ /* is not really an auth problem, but it will
+ pretty much look as such, it shouldn't really
+ happen */
+ goto done;
+ }
+
+ /* We don't actually care if the account modules fail or succeed,
+ * but we need to run them anyway because certain pam modules
+ * depend on side effects of the account modules getting run.
+ */
+ status2 = pam_acct_mgmt (pam_handle, 0);
+
+ if (gs_auth_get_verbose ())
+ {
+ g_message ("pam_acct_mgmt (...) ==> %d (%s)\n",
+ status2,
+ PAM_STRERROR (pam_handle, status2));
+ }
+
+ /* FIXME: should we handle these? */
+ switch (status2)
+ {
+ case PAM_SUCCESS:
+ break;
+ case PAM_NEW_AUTHTOK_REQD:
+ break;
+ case PAM_AUTHINFO_UNAVAIL:
+ break;
+ case PAM_ACCT_EXPIRED:
+ break;
+ case PAM_PERM_DENIED:
+ break;
+ default :
+ break;
+ }
+
+ /* Each time we successfully authenticate, refresh credentials,
+ for Kerberos/AFS/DCE/etc. If this fails, just ignore that
+ failure and blunder along; it shouldn't matter.
+
+ Note: this used to be PAM_REFRESH_CRED instead of
+ PAM_REINITIALIZE_CRED, but Jason Heiss <[email protected]>
+ says that the Linux PAM library ignores that one, and only refreshes
+ credentials when using PAM_REINITIALIZE_CRED.
+ */
+ status2 = pam_setcred (pam_handle, PAM_REINITIALIZE_CRED);
+ if (gs_auth_get_verbose ())
+ {
+ g_message (" pam_setcred (...) ==> %d (%s)",
+ status2,
+ PAM_STRERROR (pam_handle, status2));
+ }
+
+done:
+ /* we're done, close the fd and wake up the main
+ * loop
+ */
+ close (auth_operation_fd);
+
+ return status;
+}
+
+static gboolean
+gs_auth_loop_quit (GIOChannel *source,
+ GIOCondition condition,
+ gboolean *thread_done)
+{
+ *thread_done = TRUE;
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+gs_auth_pam_verify_user (pam_handle_t *handle,
+ int *status)
+{
+ GThread *auth_thread;
+ GIOChannel *channel;
+ guint watch_id;
+ int auth_operation_fds[2];
+ int auth_status;
+ gboolean thread_done;
+
+ channel = NULL;
+ watch_id = 0;
+ auth_status = PAM_AUTH_ERR;
+
+ /* This pipe gives us a set of fds we can hook into
+ * the event loop to be notified when our helper thread
+ * is ready to be reaped.
+ */
+ if (pipe (auth_operation_fds) < 0)
+ {
+ goto out;
+ }
+
+ if (fcntl (auth_operation_fds[0], F_SETFD, FD_CLOEXEC) < 0)
+ {
+ close (auth_operation_fds[0]);
+ close (auth_operation_fds[1]);
+ goto out;
+ }
+
+ if (fcntl (auth_operation_fds[1], F_SETFD, FD_CLOEXEC) < 0)
+ {
+ close (auth_operation_fds[0]);
+ close (auth_operation_fds[1]);
+ goto out;
+ }
+
+ channel = g_io_channel_unix_new (auth_operation_fds[0]);
+
+ /* we use a recursive main loop to process ui events
+ * while we wait on a thread to handle the blocking parts
+ * of pam authentication.
+ */
+ thread_done = FALSE;
+ watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP,
+ (GIOFunc) gs_auth_loop_quit, &thread_done);
+
+ auth_thread = g_thread_create ((GThreadFunc) gs_auth_thread_func,
+ GINT_TO_POINTER (auth_operation_fds[1]),
+ TRUE, NULL);
+
+ if (auth_thread == NULL)
+ {
+ goto out;
+ }
+
+ gtk_main ();
+
+ /* if the event loop was quit before the thread is done then we can't
+ * reap the thread without blocking on it finishing. The
+ * thread may not ever finish though if the pam module is blocking.
+ *
+ * The only time the event loop is going to stop when the thread isn't
+ * done, however, is if the dialog quits early (from, e.g., "cancel"),
+ * so we can just exit. An alternative option would be to switch to
+ * using pthreads directly and calling pthread_cancel.
+ */
+ if (!thread_done)
+ {
+ raise (SIGTERM);
+ }
+
+ auth_status = GPOINTER_TO_INT (g_thread_join (auth_thread));
+
+out:
+ if (watch_id != 0)
+ {
+ g_source_remove (watch_id);
+ }
+
+ if (channel != NULL)
+ {
+ g_io_channel_unref (channel);
+ }
+
+ if (status)
+ {
+ *status = auth_status;
+ }
+
+ return auth_status == PAM_SUCCESS;
+}
+
+gboolean
+gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error)
+{
+ int status = -1;
+ struct pam_conv conv;
+ struct pam_closure c;
+ struct passwd *pwent;
+
+ pwent = getpwnam (username);
+ if (pwent == NULL)
+ {
+ return FALSE;
+ }
+
+ c.username = username;
+ c.cb_func = func;
+ c.cb_data = data;
+
+ conv.conv = &pam_conversation;
+ conv.appdata_ptr = (void *) &c;
+
+ /* Initialize PAM. */
+ create_pam_handle (username, display, &conv, &status);
+ if (status != PAM_SUCCESS)
+ {
+ goto done;
+ }
+
+ pam_set_item (pam_handle, PAM_USER_PROMPT, _("Username:"));
+
+ PAM_NO_DELAY(pam_handle);
+
+ did_we_ask_for_password = FALSE;
+ if (! gs_auth_pam_verify_user (pam_handle, &status))
+ {
+ goto done;
+ }
+
+done:
+ if (status != PAM_SUCCESS)
+ {
+ set_pam_error (error, status);
+ }
+
+ close_pam_handle (status);
+
+ return (status == PAM_SUCCESS ? TRUE : FALSE);
+}
+
+gboolean
+gs_auth_init (void)
+{
+ return TRUE;
+}
+
+gboolean
+gs_auth_priv_init (void)
+{
+ /* We have nothing to do at init-time.
+ However, we might as well do some error checking.
+ If "/etc/pam.d" exists and is a directory, but "/etc/pam.d/xlock"
+ does not exist, warn that PAM probably isn't going to work.
+
+ This is a priv-init instead of a non-priv init in case the directory
+ is unreadable or something (don't know if that actually happens.)
+ */
+ const char dir [] = "/etc/pam.d";
+ const char file [] = "/etc/pam.d/" PAM_SERVICE_NAME;
+ const char file2 [] = "/etc/pam.conf";
+ struct stat st;
+
+ if (g_stat (dir, &st) == 0 && st.st_mode & S_IFDIR)
+ {
+ if (g_stat (file, &st) != 0)
+ {
+ g_warning ("%s does not exist.\n"
+ "Authentication via PAM is unlikely to work.",
+ file);
+ }
+ }
+ else if (g_stat (file2, &st) == 0)
+ {
+ FILE *f = g_fopen (file2, "r");
+ if (f)
+ {
+ gboolean ok = FALSE;
+ char buf[255];
+ while (fgets (buf, sizeof(buf), f))
+ {
+ if (strstr (buf, PAM_SERVICE_NAME))
+ {
+ ok = TRUE;
+ break;
+ }
+ }
+
+ fclose (f);
+ if (!ok)
+ {
+ g_warning ("%s does not list the `%s' service.\n"
+ "Authentication via PAM is unlikely to work.",
+ file2, PAM_SERVICE_NAME);
+ }
+ }
+ /* else warn about file2 existing but being unreadable? */
+ }
+ else
+ {
+ g_warning ("Neither %s nor %s exist.\n"
+ "Authentication via PAM is unlikely to work.",
+ file2, file);
+ }
+
+ /* Return true anyway, just in case. */
+ return TRUE;
+}
diff --git a/src/gs-auth-pwent.c b/src/gs-auth-pwent.c
new file mode 100644
index 0000000..3fe1b22
--- /dev/null
+++ b/src/gs-auth-pwent.c
@@ -0,0 +1,291 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (c) 1993-1998 Jamie Zawinski <[email protected]>
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_CRYPT_H
+# include <crypt.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef __bsdi__
+# include <sys/param.h>
+# if _BSDI_VERSION >= 199608
+# define BSD_AUTH
+# endif
+#endif /* __bsdi__ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#if defined(HAVE_SHADOW_PASSWD) /* passwds live in /etc/shadow */
+
+# include <shadow.h>
+# define PWTYPE struct spwd *
+# define PWPSLOT sp_pwdp
+# define GETPW getspnam
+
+#elif defined(HAVE_ENHANCED_PASSWD) /* passwds live in /tcb/files/auth/ */
+/* M.Matsumoto <[email protected]> */
+# include <sys/security.h>
+# include <prot.h>
+
+# define PWTYPE struct pr_passwd *
+# define PWPSLOT ufld.fd_encrypt
+# define GETPW getprpwnam
+
+#elif defined(HAVE_ADJUNCT_PASSWD)
+
+# include <sys/label.h>
+# include <sys/audit.h>
+# include <pwdadj.h>
+
+# define PWTYPE struct passwd_adjunct *
+# define PWPSLOT pwa_passwd
+# define GETPW getpwanam
+
+#elif defined(HAVE_HPUX_PASSWD)
+
+# include <hpsecurity.h>
+# include <prot.h>
+
+# define PWTYPE struct s_passwd *
+# define PWPSLOT pw_passwd
+# define GETPW getspwnam
+
+# define HAVE_BIGCRYPT
+
+#endif
+
+#include "gs-auth.h"
+
+static gboolean verbose_enabled = FALSE;
+
+static char *encrypted_user_passwd = NULL;
+
+GQuark
+gs_auth_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (! quark)
+ {
+ quark = g_quark_from_static_string ("gs_auth_error");
+ }
+
+ return quark;
+}
+
+void
+gs_auth_set_verbose (gboolean enabled)
+{
+ verbose_enabled = enabled;
+}
+
+gboolean
+gs_auth_get_verbose (void)
+{
+ return verbose_enabled;
+}
+
+static gboolean
+passwd_known (const char *pw)
+{
+ return (pw &&
+ pw[0] != '*' && /* This would be sensible... */
+ strlen (pw) > 4); /* ...but this is what Solaris does. */
+}
+
+static char *
+get_encrypted_passwd (const char *user)
+{
+ char *result = NULL;
+
+#ifdef PWTYPE
+ if (user && *user && !result)
+ {
+ /* First check the shadow passwords. */
+ PWTYPE p = GETPW ((char *) user);
+ if (p && passwd_known (p->PWPSLOT))
+ {
+ result = g_strdup (p->PWPSLOT);
+ }
+ }
+#endif /* PWTYPE */
+
+ if (user && *user && !result)
+ {
+ /* Check non-shadow passwords too. */
+ struct passwd *p = getpwnam (user);
+ if (p && passwd_known (p->pw_passwd))
+ {
+ result = g_strdup (p->pw_passwd);
+ }
+ }
+
+ /* The manual for passwd(4) on HPUX 10.10 says:
+
+ Password aging is put in effect for a particular user if his
+ encrypted password in the password file is followed by a comma and
+ a nonnull string of characters from the above alphabet. This
+ string defines the "age" needed to implement password aging.
+
+ So this means that passwd->pw_passwd isn't simply a string of cyphertext,
+ it might have trailing junk. So, if there is a comma in the string, and
+ that comma is beyond position 13, terminate the string before the comma.
+ */
+ if (result && strlen (result) > 13)
+ {
+ char *s = strchr (result + 13, ',');
+ if (s)
+ {
+ *s = 0;
+ }
+ }
+
+#ifndef HAVE_PAM
+ /* We only issue this warning if not compiled with support for PAM.
+ If we're using PAM, it's not unheard of that normal pwent passwords
+ would be unavailable. */
+
+ if (!result)
+ {
+ g_warning ("Couldn't get password of \"%s\"",
+ (user ? user : "(null)"));
+ }
+
+#endif /* !HAVE_PAM */
+
+ return result;
+}
+
+/* This has to be called before we've changed our effective user ID,
+ because it might need privileges to get at the encrypted passwords.
+ Returns false if we weren't able to get any passwords, and therefore,
+ locking isn't possible. (It will also have written to stderr.)
+*/
+
+gboolean
+gs_auth_priv_init (void)
+{
+ const char *u;
+
+ u = g_get_user_name ();
+
+ encrypted_user_passwd = get_encrypted_passwd (u);
+
+ if (encrypted_user_passwd != NULL)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+gboolean
+gs_auth_init (void)
+{
+ if (encrypted_user_passwd != NULL)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static gboolean
+passwds_match (const char *cleartext,
+ const char *ciphertext)
+{
+ char *s = NULL; /* note that on some systems, crypt() may return null */
+
+ s = (char *) crypt (cleartext, ciphertext);
+ if (s && !strcmp (s, ciphertext))
+ {
+ return TRUE;
+ }
+
+#ifdef HAVE_BIGCRYPT
+ /* There seems to be no way to tell at runtime if an HP machine is in
+ "trusted" mode, and thereby, which of crypt() or bigcrypt() we should
+ be calling to compare passwords. So call them both, and see which
+ one works. */
+
+ s = (char *) bigcrypt (cleartext, ciphertext);
+ if (s && !strcmp (s, ciphertext))
+ {
+ return TRUE;
+ }
+
+#endif /* HAVE_BIGCRYPT */
+
+ return FALSE;
+}
+
+gboolean
+gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error)
+{
+ char *password;
+
+ password = NULL;
+
+ /* ask for the password for user */
+ if (func != NULL)
+ {
+ func (GS_AUTH_MESSAGE_PROMPT_ECHO_OFF,
+ "Password: ",
+ &password,
+ data);
+ }
+
+ if (password == NULL)
+ {
+ return FALSE;
+ }
+
+ if (encrypted_user_passwd && passwds_match (password, encrypted_user_passwd))
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
diff --git a/src/gs-auth-pwent.c.orig b/src/gs-auth-pwent.c.orig
new file mode 100644
index 0000000..21abbee
--- /dev/null
+++ b/src/gs-auth-pwent.c.orig
@@ -0,0 +1,291 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (c) 1993-1998 Jamie Zawinski <[email protected]>
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_CRYPT_H
+# include <crypt.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifdef __bsdi__
+# include <sys/param.h>
+# if _BSDI_VERSION >= 199608
+# define BSD_AUTH
+# endif
+#endif /* __bsdi__ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#if defined(HAVE_SHADOW_PASSWD) /* passwds live in /etc/shadow */
+
+# include <shadow.h>
+# define PWTYPE struct spwd *
+# define PWPSLOT sp_pwdp
+# define GETPW getspnam
+
+#elif defined(HAVE_ENHANCED_PASSWD) /* passwds live in /tcb/files/auth/ */
+/* M.Matsumoto <[email protected]> */
+# include <sys/security.h>
+# include <prot.h>
+
+# define PWTYPE struct pr_passwd *
+# define PWPSLOT ufld.fd_encrypt
+# define GETPW getprpwnam
+
+#elif defined(HAVE_ADJUNCT_PASSWD)
+
+# include <sys/label.h>
+# include <sys/audit.h>
+# include <pwdadj.h>
+
+# define PWTYPE struct passwd_adjunct *
+# define PWPSLOT pwa_passwd
+# define GETPW getpwanam
+
+#elif defined(HAVE_HPUX_PASSWD)
+
+# include <hpsecurity.h>
+# include <prot.h>
+
+# define PWTYPE struct s_passwd *
+# define PWPSLOT pw_passwd
+# define GETPW getspwnam
+
+# define HAVE_BIGCRYPT
+
+#endif
+
+#include "gs-auth.h"
+
+static gboolean verbose_enabled = FALSE;
+
+static char *encrypted_user_passwd = NULL;
+
+GQuark
+gs_auth_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (! quark)
+ {
+ quark = g_quark_from_static_string ("gs_auth_error");
+ }
+
+ return quark;
+}
+
+void
+gs_auth_set_verbose (gboolean enabled)
+{
+ verbose_enabled = enabled;
+}
+
+gboolean
+gs_auth_get_verbose (void)
+{
+ return verbose_enabled;
+}
+
+static gboolean
+passwd_known (const char *pw)
+{
+ return (pw &&
+ pw[0] != '*' && /* This would be sensible... */
+ strlen (pw) > 4); /* ...but this is what Solaris does. */
+}
+
+static char *
+get_encrypted_passwd (const char *user)
+{
+ char *result = NULL;
+
+#ifdef PWTYPE
+ if (user && *user && !result)
+ {
+ /* First check the shadow passwords. */
+ PWTYPE p = GETPW ((char *) user);
+ if (p && passwd_known (p->PWPSLOT))
+ {
+ result = g_strdup (p->PWPSLOT);
+ }
+ }
+#endif /* PWTYPE */
+
+ if (user && *user && !result)
+ {
+ /* Check non-shadow passwords too. */
+ struct passwd *p = getpwnam (user);
+ if (p && passwd_known (p->pw_passwd))
+ {
+ result = g_strdup (p->pw_passwd);
+ }
+ }
+
+ /* The manual for passwd(4) on HPUX 10.10 says:
+
+ Password aging is put in effect for a particular user if his
+ encrypted password in the password file is followed by a comma and
+ a nonnull string of characters from the above alphabet. This
+ string defines the "age" needed to implement password aging.
+
+ So this means that passwd->pw_passwd isn't simply a string of cyphertext,
+ it might have trailing junk. So, if there is a comma in the string, and
+ that comma is beyond position 13, terminate the string before the comma.
+ */
+ if (result && strlen (result) > 13)
+ {
+ char *s = strchr (result + 13, ',');
+ if (s)
+ {
+ *s = 0;
+ }
+ }
+
+#ifndef HAVE_PAM
+ /* We only issue this warning if not compiled with support for PAM.
+ If we're using PAM, it's not unheard of that normal pwent passwords
+ would be unavailable. */
+
+ if (!result)
+ {
+ g_warning ("Couldn't get password of \"%s\"",
+ (user ? user : "(null)"));
+ }
+
+#endif /* !HAVE_PAM */
+
+ return result;
+}
+
+/* This has to be called before we've changed our effective user ID,
+ because it might need privileges to get at the encrypted passwords.
+ Returns false if we weren't able to get any passwords, and therefore,
+ locking isn't possible. (It will also have written to stderr.)
+*/
+
+gboolean
+gs_auth_priv_init (void)
+{
+ const char *u;
+
+ u = g_get_user_name ();
+
+ encrypted_user_passwd = get_encrypted_passwd (u);
+
+ if (encrypted_user_passwd != NULL)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+gboolean
+gs_auth_init (void)
+{
+ if (encrypted_user_passwd != NULL)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static gboolean
+passwds_match (const char *cleartext,
+ const char *ciphertext)
+{
+ char *s = NULL; /* note that on some systems, crypt() may return null */
+
+ s = (char *) crypt (cleartext, ciphertext);
+ if (s && !strcmp (s, ciphertext))
+ {
+ return TRUE;
+ }
+
+#ifdef HAVE_BIGCRYPT
+ /* There seems to be no way to tell at runtime if an HP machine is in
+ "trusted" mode, and thereby, which of crypt() or bigcrypt() we should
+ be calling to compare passwords. So call them both, and see which
+ one works. */
+
+ s = (char *) bigcrypt (cleartext, ciphertext);
+ if (s && !strcmp (s, ciphertext))
+ {
+ return TRUE;
+ }
+
+#endif /* HAVE_BIGCRYPT */
+
+ return FALSE;
+}
+
+gboolean
+gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error)
+{
+ char *password;
+
+ password = NULL;
+
+ /* ask for the password for user */
+ if (func != NULL)
+ {
+ func (GS_AUTH_MESSAGE_PROMPT_ECHO_OFF,
+ "Password: ",
+ &password,
+ data);
+ }
+
+ if (password == NULL)
+ {
+ return FALSE;
+ }
+
+ if (encrypted_user_passwd && passwds_match (password, encrypted_user_passwd))
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
diff --git a/src/gs-auth.h b/src/gs-auth.h
new file mode 100644
index 0000000..503e92d
--- /dev/null
+++ b/src/gs-auth.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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 __GS_AUTH_H
+#define __GS_AUTH_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GS_AUTH_MESSAGE_PROMPT_ECHO_ON,
+ GS_AUTH_MESSAGE_PROMPT_ECHO_OFF,
+ GS_AUTH_MESSAGE_ERROR_MSG,
+ GS_AUTH_MESSAGE_TEXT_INFO
+} GSAuthMessageStyle;
+
+typedef enum
+{
+ GS_AUTH_ERROR_GENERAL,
+ GS_AUTH_ERROR_AUTH_ERROR,
+ GS_AUTH_ERROR_USER_UNKNOWN,
+ GS_AUTH_ERROR_AUTH_DENIED
+} GSAuthError;
+
+typedef gboolean (* GSAuthMessageFunc) (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data);
+
+#define GS_AUTH_ERROR gs_auth_error_quark ()
+
+GQuark gs_auth_error_quark (void);
+
+void gs_auth_set_verbose (gboolean verbose);
+gboolean gs_auth_get_verbose (void);
+
+gboolean gs_auth_priv_init (void);
+gboolean gs_auth_init (void);
+gboolean gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GS_AUTH_H */
diff --git a/src/gs-auth.h.orig b/src/gs-auth.h.orig
new file mode 100644
index 0000000..25e5271
--- /dev/null
+++ b/src/gs-auth.h.orig
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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 __GS_AUTH_H
+#define __GS_AUTH_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GS_AUTH_MESSAGE_PROMPT_ECHO_ON,
+ GS_AUTH_MESSAGE_PROMPT_ECHO_OFF,
+ GS_AUTH_MESSAGE_ERROR_MSG,
+ GS_AUTH_MESSAGE_TEXT_INFO
+} GSAuthMessageStyle;
+
+typedef enum {
+ GS_AUTH_ERROR_GENERAL,
+ GS_AUTH_ERROR_AUTH_ERROR,
+ GS_AUTH_ERROR_USER_UNKNOWN,
+ GS_AUTH_ERROR_AUTH_DENIED
+} GSAuthError;
+
+typedef gboolean (* GSAuthMessageFunc) (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data);
+
+#define GS_AUTH_ERROR gs_auth_error_quark ()
+
+GQuark gs_auth_error_quark (void);
+
+void gs_auth_set_verbose (gboolean verbose);
+gboolean gs_auth_get_verbose (void);
+
+gboolean gs_auth_priv_init (void);
+gboolean gs_auth_init (void);
+gboolean gs_auth_verify_user (const char *username,
+ const char *display,
+ GSAuthMessageFunc func,
+ gpointer data,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* __GS_AUTH_H */
diff --git a/src/gs-debug.c b/src/gs-debug.c
new file mode 100644
index 0000000..52fbc12
--- /dev/null
+++ b/src/gs-debug.c
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gs-debug.h"
+
+static gboolean debugging = FALSE;
+static FILE *debug_out = NULL;
+
+/* Based on rhythmbox/lib/rb-debug.c */
+/* Our own funky debugging function, should only be used when something
+ * is not going wrong, if something *is* wrong use g_warning.
+ */
+void
+gs_debug_real (const char *func,
+ const char *file,
+ const int line,
+ const char *format, ...)
+{
+ va_list args;
+ char buffer [1025];
+ char *str_time;
+ time_t the_time;
+
+ if (debugging == FALSE)
+ return;
+
+ va_start (args, format);
+
+ g_vsnprintf (buffer, 1024, format, args);
+
+ va_end (args);
+
+ time (&the_time);
+ str_time = g_new0 (char, 255);
+ strftime (str_time, 254, "%H:%M:%S", localtime (&the_time));
+
+ fprintf ((debug_out ? debug_out : stderr),
+ "[%s] %s:%d (%s):\t %s\n",
+ func, file, line, str_time, buffer);
+
+ if (debug_out)
+ fflush (debug_out);
+
+ g_free (str_time);
+}
+
+gboolean
+gs_debug_enabled (void)
+{
+ return debugging;
+}
+
+void
+gs_debug_init (gboolean debug,
+ gboolean to_file)
+{
+ /* return if already initialized */
+ if (debugging == TRUE)
+ {
+ return;
+ }
+
+ debugging = debug;
+
+ if (debug && to_file)
+ {
+ const char path [50] = "mate_screensaver_debug_XXXXXX";
+ int fd;
+
+ fd = g_file_open_tmp (path, NULL, NULL);
+
+ if (fd >= 0)
+ {
+ debug_out = fdopen (fd, "a");
+ }
+ }
+
+ gs_debug ("Debugging %s", (debug) ? "enabled" : "disabled");
+}
+
+void
+gs_debug_shutdown (void)
+{
+ if (! debugging)
+ return;
+
+ gs_debug ("Shutting down debugging");
+
+ debugging = FALSE;
+
+ if (debug_out != NULL)
+ {
+ fclose (debug_out);
+ debug_out = NULL;
+ }
+}
+
+void
+_gs_profile_log (const char *func,
+ const char *note,
+ const char *format,
+ ...)
+{
+ va_list args;
+ char *str;
+ char *formatted;
+
+ if (format == NULL)
+ {
+ formatted = g_strdup ("");
+ }
+ else
+ {
+ va_start (args, format);
+ formatted = g_strdup_vprintf (format, args);
+ va_end (args);
+ }
+
+ if (func != NULL)
+ {
+ str = g_strdup_printf ("MARK: %s %s: %s %s", g_get_prgname(), func, note ? note : "", formatted);
+ }
+ else
+ {
+ str = g_strdup_printf ("MARK: %s: %s %s", g_get_prgname(), note ? note : "", formatted);
+ }
+
+ g_free (formatted);
+
+ g_access (str, F_OK);
+ g_free (str);
+}
diff --git a/src/gs-debug.c.orig b/src/gs-debug.c.orig
new file mode 100644
index 0000000..ff80c0c
--- /dev/null
+++ b/src/gs-debug.c.orig
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "gs-debug.h"
+
+static gboolean debugging = FALSE;
+static FILE *debug_out = NULL;
+
+/* Based on rhythmbox/lib/rb-debug.c */
+/* Our own funky debugging function, should only be used when something
+ * is not going wrong, if something *is* wrong use g_warning.
+ */
+void
+gs_debug_real (const char *func,
+ const char *file,
+ const int line,
+ const char *format, ...)
+{
+ va_list args;
+ char buffer [1025];
+ char *str_time;
+ time_t the_time;
+
+ if (debugging == FALSE)
+ return;
+
+ va_start (args, format);
+
+ g_vsnprintf (buffer, 1024, format, args);
+
+ va_end (args);
+
+ time (&the_time);
+ str_time = g_new0 (char, 255);
+ strftime (str_time, 254, "%H:%M:%S", localtime (&the_time));
+
+ fprintf ((debug_out ? debug_out : stderr),
+ "[%s] %s:%d (%s):\t %s\n",
+ func, file, line, str_time, buffer);
+
+ if (debug_out)
+ fflush (debug_out);
+
+ g_free (str_time);
+}
+
+gboolean
+gs_debug_enabled (void)
+{
+ return debugging;
+}
+
+void
+gs_debug_init (gboolean debug,
+ gboolean to_file)
+{
+ /* return if already initialized */
+ if (debugging == TRUE)
+ {
+ return;
+ }
+
+ debugging = debug;
+
+ if (debug && to_file)
+ {
+ const char path [50] = "mate_screensaver_debug_XXXXXX";
+ int fd;
+
+ fd = g_file_open_tmp (path, NULL, NULL);
+
+ if (fd >= 0)
+ {
+ debug_out = fdopen (fd, "a");
+ }
+ }
+
+ gs_debug ("Debugging %s", (debug) ? "enabled" : "disabled");
+}
+
+void
+gs_debug_shutdown (void)
+{
+ if (! debugging)
+ return;
+
+ gs_debug ("Shutting down debugging");
+
+ debugging = FALSE;
+
+ if (debug_out != NULL)
+ {
+ fclose (debug_out);
+ debug_out = NULL;
+ }
+}
+
+void
+_gs_profile_log (const char *func,
+ const char *note,
+ const char *format,
+ ...)
+{
+ va_list args;
+ char *str;
+ char *formatted;
+
+ if (format == NULL)
+ {
+ formatted = g_strdup ("");
+ }
+ else
+ {
+ va_start (args, format);
+ formatted = g_strdup_vprintf (format, args);
+ va_end (args);
+ }
+
+ if (func != NULL)
+ {
+ str = g_strdup_printf ("MARK: %s %s: %s %s", g_get_prgname(), func, note ? note : "", formatted);
+ }
+ else
+ {
+ str = g_strdup_printf ("MARK: %s: %s %s", g_get_prgname(), note ? note : "", formatted);
+ }
+
+ g_free (formatted);
+
+ g_access (str, F_OK);
+ g_free (str);
+}
diff --git a/src/gs-debug.h b/src/gs-debug.h
new file mode 100644
index 0000000..fdf9fd8
--- /dev/null
+++ b/src/gs-debug.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_DEBUG_H
+#define __GS_DEBUG_H
+
+#include <stdarg.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define gs_debug(...) gs_debug_real (__func__, __FILE__, __LINE__, __VA_ARGS__)
+#elif defined(__GNUC__) && __GNUC__ >= 3
+#define gs_debug(...) gs_debug_real (__FUNCTION__, __FILE__, __LINE__, __VA_ARGS__)
+#else
+#define gs_debug(...)
+#endif
+
+void gs_debug_init (gboolean debug,
+ gboolean to_file);
+gboolean gs_debug_enabled (void);
+void gs_debug_shutdown (void);
+void gs_debug_real (const char *func,
+ const char *file,
+ int line,
+ const char *format, ...);
+
+#ifdef ENABLE_PROFILING
+#ifdef G_HAVE_ISO_VARARGS
+#define gs_profile_start(...) _gs_profile_log (G_STRFUNC, "start", __VA_ARGS__)
+#define gs_profile_end(...) _gs_profile_log (G_STRFUNC, "end", __VA_ARGS__)
+#define gs_profile_msg(...) _gs_profile_log (NULL, NULL, __VA_ARGS__)
+#elif defined(G_HAVE_GNUC_VARARGS)
+#define gs_profile_start(format...) _gs_profile_log (G_STRFUNC, "start", format)
+#define gs_profile_end(format...) _gs_profile_log (G_STRFUNC, "end", format)
+#define gs_profile_msg(format...) _gs_profile_log (NULL, NULL, format)
+#endif
+#else
+#define gs_profile_start(...)
+#define gs_profile_end(...)
+#define gs_profile_msg(...)
+#endif
+
+void _gs_profile_log (const char *func,
+ const char *note,
+ const char *format,
+ ...) G_GNUC_PRINTF (3, 4);
+
+G_END_DECLS
+
+#endif /* __GS_DEBUG_H */
diff --git a/src/gs-fade.c b/src/gs-fade.c
new file mode 100644
index 0000000..ba04b67
--- /dev/null
+++ b/src/gs-fade.c
@@ -0,0 +1,987 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2009 William Jon McCann <[email protected]>
+ * Copyright (C) 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-fade.h"
+#include "gs-debug.h"
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+
+#include "libmateui/mate-rr.h"
+
+/* XFree86 4.x+ Gamma fading */
+
+
+#ifdef HAVE_XF86VMODE_GAMMA
+
+#include <X11/extensions/xf86vmode.h>
+
+#define XF86_MIN_GAMMA 0.1
+
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void gs_fade_class_init (GSFadeClass *klass);
+static void gs_fade_init (GSFade *fade);
+static void gs_fade_finalize (GObject *object);
+
+#define GS_FADE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_FADE, GSFadePrivate))
+
+struct GSGammaInfo
+{
+ int size;
+ unsigned short *r;
+ unsigned short *g;
+ unsigned short *b;
+};
+
+struct GSFadeScreenPrivate
+{
+ int fade_type;
+ int num_ramps;
+ /* one per crtc in randr mode */
+ struct GSGammaInfo *info;
+ /* one per screen in theory */
+ MateRRScreen *rrscreen;
+#ifdef HAVE_XF86VMODE_GAMMA
+ /* one per screen also */
+ XF86VidModeGamma vmg;
+#endif /* HAVE_XF86VMODE_GAMMA */
+ gboolean (*fade_setup) (GSFade *fade,
+ int screen);
+ gboolean (*fade_set_alpha_gamma) (GSFade *fade,
+ int screen,
+ gdouble alpha);
+ void (*fade_finish) (GSFade *fade,
+ int screen);
+};
+
+struct GSFadePrivate
+{
+ guint enabled : 1;
+ guint active : 1;
+
+ guint timeout;
+
+ guint step;
+ guint num_steps;
+ guint timer_id;
+
+ gdouble alpha_per_iter;
+ gdouble current_alpha;
+
+ int num_screens;
+
+ struct GSFadeScreenPrivate *screen_priv;
+};
+
+enum
+{
+ FADED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ FADE_TYPE_NONE,
+ FADE_TYPE_GAMMA_NUMBER,
+ FADE_TYPE_GAMMA_RAMP,
+ FADE_TYPE_XRANDR,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSFade, gs_fade, G_TYPE_OBJECT)
+
+static gpointer fade_object = NULL;
+
+#ifdef HAVE_XF86VMODE_GAMMA
+
+/* This is needed because the VidMode extension doesn't work
+ on remote displays -- but if the remote display has the extension
+ at all, XF86VidModeQueryExtension returns true, and then
+ XF86VidModeQueryVersion dies with an X error.
+*/
+
+static gboolean error_handler_hit = FALSE;
+
+static int
+ignore_all_errors_ehandler (Display *dpy,
+ XErrorEvent *error)
+{
+ error_handler_hit = TRUE;
+
+ return 0;
+}
+
+static Bool
+safe_XF86VidModeQueryVersion (Display *dpy,
+ int *majP,
+ int *minP)
+{
+ Bool result;
+ XErrorHandler old_handler;
+
+ XSync (dpy, False);
+ error_handler_hit = FALSE;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ result = XF86VidModeQueryVersion (dpy, majP, minP);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return (error_handler_hit
+ ? False
+ : result);
+}
+
+static gboolean
+xf86_whack_gamma (int screen,
+ struct GSFadeScreenPrivate *screen_priv,
+ float ratio)
+{
+ Bool status;
+ struct GSGammaInfo *gamma_info;
+
+ gamma_info = screen_priv->info;
+
+ if (!gamma_info)
+ return FALSE;
+
+ if (ratio < 0)
+ {
+ ratio = 0;
+ }
+ if (ratio > 1)
+ {
+ ratio = 1;
+ }
+
+ if (gamma_info->size == 0)
+ {
+ /* we only have a gamma number, not a ramp. */
+
+ XF86VidModeGamma g2;
+
+ g2.red = screen_priv->vmg.red * ratio;
+ g2.green = screen_priv->vmg.green * ratio;
+ g2.blue = screen_priv->vmg.blue * ratio;
+
+ if (g2.red < XF86_MIN_GAMMA)
+ {
+ g2.red = XF86_MIN_GAMMA;
+ }
+ if (g2.green < XF86_MIN_GAMMA)
+ {
+ g2.green = XF86_MIN_GAMMA;
+ }
+ if (g2.blue < XF86_MIN_GAMMA)
+ {
+ g2.blue = XF86_MIN_GAMMA;
+ }
+
+ status = XF86VidModeSetGamma (GDK_DISPLAY (), screen, &g2);
+ }
+ else
+ {
+
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+ unsigned short *r, *g, *b;
+ int i;
+
+ r = g_new0 (unsigned short, gamma_info->size);
+ g = g_new0 (unsigned short, gamma_info->size);
+ b = g_new0 (unsigned short, gamma_info->size);
+
+ for (i = 0; i < gamma_info->size; i++)
+ {
+ r[i] = gamma_info->r[i] * ratio;
+ g[i] = gamma_info->g[i] * ratio;
+ b[i] = gamma_info->b[i] * ratio;
+ }
+
+ status = XF86VidModeSetGammaRamp (GDK_DISPLAY (), screen, gamma_info->size, r, g, b);
+
+ g_free (r);
+ g_free (g);
+ g_free (b);
+
+# else /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ abort ();
+# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ }
+
+ gdk_flush ();
+
+ return status;
+}
+
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+/* VidModeExtension version 2.0 or better is needed to do gamma.
+ 2.0 added gamma values; 2.1 added gamma ramps.
+*/
+# define XF86_VIDMODE_GAMMA_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_MIN_MINOR 0
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1
+
+
+gboolean
+gs_fade_get_enabled (GSFade *fade)
+{
+ g_return_val_if_fail (GS_IS_FADE (fade), FALSE);
+
+ return fade->priv->enabled;
+}
+
+void
+gs_fade_set_enabled (GSFade *fade,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ if (fade->priv->enabled != enabled)
+ {
+ fade->priv->enabled = enabled;
+ }
+}
+
+#ifdef HAVE_XF86VMODE_GAMMA
+static gboolean
+gamma_fade_setup (GSFade *fade, int screen_idx)
+{
+ gboolean res;
+ struct GSFadeScreenPrivate *screen_priv;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (screen_priv->info)
+ return TRUE;
+
+# ifndef HAVE_XF86VMODE_GAMMA_RAMP
+ if (FADE_TYPE_GAMMA_RAMP == screen_priv->fade_type)
+ {
+ /* server is newer than client! */
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ }
+# endif
+
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+
+ screen_priv->info = g_new0(struct GSGammaInfo, 1);
+ screen_priv->num_ramps = 1;
+
+ if (FADE_TYPE_GAMMA_RAMP == screen_priv->fade_type)
+ {
+ /* have ramps */
+
+ res = XF86VidModeGetGammaRampSize (GDK_DISPLAY (), screen_idx, &screen_priv->info->size);
+ if (!res || screen_priv->info->size <= 0)
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+
+ screen_priv->info->r = g_new0 (unsigned short, screen_priv->info->size);
+ screen_priv->info->g = g_new0 (unsigned short, screen_priv->info->size);
+ screen_priv->info->b = g_new0 (unsigned short, screen_priv->info->size);
+
+ if (! (screen_priv->info->r && screen_priv->info->g && screen_priv->info->b))
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+
+ res = XF86VidModeGetGammaRamp (GDK_DISPLAY (),
+ screen_idx,
+ screen_priv->info->size,
+ screen_priv->info->r,
+ screen_priv->info->g,
+ screen_priv->info->b);
+ if (! res)
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+ gs_debug ("Initialized gamma ramp fade");
+ }
+# endif /* HAVE_XF86VMODE_GAMMA_RAMP */
+
+test_number:
+ if (FADE_TYPE_GAMMA_NUMBER == screen_priv->fade_type)
+ {
+ /* only have gamma parameter, not ramps. */
+
+ res = XF86VidModeGetGamma (GDK_DISPLAY (), screen_idx, &screen_priv->vmg);
+ if (! res)
+ {
+ screen_priv->fade_type = FADE_TYPE_NONE;
+ goto test_none;
+ }
+ gs_debug ("Initialized gamma fade for screen %d: %f %f %f",
+ screen_idx,
+ screen_priv->vmg.red,
+ screen_priv->vmg.green,
+ screen_priv->vmg.blue);
+ }
+
+test_none:
+ if (FADE_TYPE_NONE == screen_priv->fade_type)
+ {
+ goto FAIL;
+ }
+
+ return TRUE;
+FAIL:
+
+ return FALSE;
+}
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void
+screen_fade_finish (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ int i;
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (!screen_priv->info)
+ return;
+
+ for (i = 0; i < screen_priv->num_ramps; i++)
+ {
+ if (screen_priv->info[i].r)
+ g_free (screen_priv->info[i].r);
+ if (screen_priv->info[i].g)
+ g_free (screen_priv->info[i].g);
+ if (screen_priv->info[i].b)
+ g_free (screen_priv->info[i].b);
+ }
+
+ g_free (screen_priv->info);
+ screen_priv->info = NULL;
+ screen_priv->num_ramps = 0;
+}
+
+#ifdef HAVE_XF86VMODE_GAMMA
+static gboolean
+gamma_fade_set_alpha_gamma (GSFade *fade,
+ int screen_idx,
+ gdouble alpha)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ gboolean res;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+ res = xf86_whack_gamma (screen_idx, screen_priv, alpha);
+
+ return TRUE;
+}
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void
+check_gamma_extension (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+#ifdef HAVE_XF86VMODE_GAMMA
+ int event;
+ int error;
+ int major;
+ int minor;
+ gboolean res;
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+#ifdef HAVE_XF86VMODE_GAMMA
+ res = XF86VidModeQueryExtension (GDK_DISPLAY (), &event, &error);
+ if (! res)
+ goto fade_none;
+
+ res = safe_XF86VidModeQueryVersion (GDK_DISPLAY (), &major, &minor);
+ if (! res)
+ goto fade_none;
+
+ if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_MIN_MINOR))
+ goto fade_none;
+
+ screen_priv->fade_setup = gamma_fade_setup;
+ screen_priv->fade_finish = screen_fade_finish;
+ screen_priv->fade_set_alpha_gamma = gamma_fade_set_alpha_gamma;
+
+ if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR))
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ return;
+ }
+
+ /* Copacetic */
+ screen_priv->fade_type = FADE_TYPE_GAMMA_RAMP;
+ return;
+fade_none:
+#endif
+ screen_priv->fade_type = FADE_TYPE_NONE;
+}
+
+/* Xrandr support */
+
+static gboolean xrandr_fade_setup (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ MateRRCrtc *crtc;
+ MateRRCrtc **crtcs;
+ int crtc_count = 0;
+ struct GSGammaInfo *info;
+ gboolean res;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (screen_priv->info)
+ return TRUE;
+
+ /* refresh the screen info */
+ mate_rr_screen_refresh (screen_priv->rrscreen, NULL);
+
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ while (*crtcs)
+ {
+ crtc_count++;
+ crtcs++;
+ };
+
+ screen_priv->info = g_new0 (struct GSGammaInfo, crtc_count);
+ screen_priv->num_ramps = crtc_count;
+
+ crtc_count = 0;
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ while (*crtcs)
+ {
+ crtc = *crtcs;
+
+ info = &screen_priv->info[crtc_count];
+
+ /* if no mode ignore crtc */
+ if (!mate_rr_crtc_get_current_mode (crtc))
+ {
+ info->size = 0;
+ info->r = NULL;
+ info->g = NULL;
+ info->b = NULL;
+ }
+ else
+ {
+ res = mate_rr_crtc_get_gamma (crtc, &info->size,
+ &info->r, &info->g,
+ &info->b);
+ if (res == FALSE)
+ goto fail;
+ }
+
+ crtcs++;
+ crtc_count++;
+ }
+ return TRUE;
+fail:
+ return FALSE;
+}
+
+static void xrandr_crtc_whack_gamma (MateRRCrtc *crtc,
+ struct GSGammaInfo *gamma_info,
+ float ratio)
+{
+ unsigned short *r, *g, *b;
+ int i;
+
+ if (gamma_info->size == 0)
+ return;
+
+ if (ratio < 0)
+ {
+ ratio = 0;
+ }
+ if (ratio > 1)
+ {
+ ratio = 1;
+ }
+
+ r = g_new0 (unsigned short, gamma_info->size);
+ g = g_new0 (unsigned short, gamma_info->size);
+ b = g_new0 (unsigned short, gamma_info->size);
+
+ for (i = 0; i < gamma_info->size; i++)
+ {
+ r[i] = gamma_info->r[i] * ratio;
+ g[i] = gamma_info->g[i] * ratio;
+ b[i] = gamma_info->b[i] * ratio;
+ }
+
+ mate_rr_crtc_set_gamma (crtc, gamma_info->size,
+ r, g, b);
+ g_free (r);
+ g_free (g);
+ g_free (b);
+}
+
+static gboolean xrandr_fade_set_alpha_gamma (GSFade *fade,
+ int screen_idx,
+ gdouble alpha)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ struct GSGammaInfo *info;
+ MateRRCrtc **crtcs;
+ int i;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (!screen_priv->info)
+ return FALSE;
+
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ i = 0;
+
+ while (*crtcs)
+ {
+ info = &screen_priv->info[i];
+ xrandr_crtc_whack_gamma (*crtcs, info, alpha);
+ i++;
+ crtcs++;
+ }
+ return TRUE;
+}
+
+static void
+check_randr_extension (GSFade *fade, int screen_idx)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ GdkScreen *screen = gdk_display_get_screen (display, screen_idx);
+ struct GSFadeScreenPrivate *screen_priv;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ screen_priv->rrscreen = mate_rr_screen_new (screen,
+ NULL,
+ NULL,
+ NULL);
+ if (!screen_priv->rrscreen)
+ {
+ screen_priv->fade_type = FADE_TYPE_NONE;
+ return;
+ }
+
+ screen_priv->fade_type = FADE_TYPE_XRANDR;
+ screen_priv->fade_setup = xrandr_fade_setup;
+ screen_priv->fade_finish = screen_fade_finish;
+ screen_priv->fade_set_alpha_gamma = xrandr_fade_set_alpha_gamma;
+}
+
+static gboolean
+gs_fade_set_alpha (GSFade *fade,
+ gdouble alpha)
+{
+ gboolean ret = FALSE;
+ int i;
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ switch (fade->priv->screen_priv[i].fade_type)
+ {
+ case FADE_TYPE_GAMMA_RAMP:
+ case FADE_TYPE_GAMMA_NUMBER:
+ case FADE_TYPE_XRANDR:
+ ret = fade->priv->screen_priv[i].fade_set_alpha_gamma (fade, i, alpha);
+ break;
+ case FADE_TYPE_NONE:
+ ret = FALSE;
+ break;
+ default:
+ g_warning ("Unknown fade type");
+ ret = FALSE;
+ break;
+ }
+ }
+ return ret;
+}
+
+static gboolean
+gs_fade_out_iter (GSFade *fade)
+{
+ gboolean ret;
+
+ if (fade->priv->current_alpha < 0.01)
+ {
+ return FALSE;
+ }
+
+ fade->priv->current_alpha -= fade->priv->alpha_per_iter;
+
+ ret = gs_fade_set_alpha (fade, fade->priv->current_alpha);
+
+ return ret;
+}
+
+static gboolean
+gs_fade_stop (GSFade *fade)
+{
+ if (fade->priv->timer_id > 0)
+ {
+ g_source_remove (fade->priv->timer_id);
+ fade->priv->timer_id = 0;
+ }
+
+ fade->priv->step = 0;
+ fade->priv->active = FALSE;
+
+ return TRUE;
+}
+
+void
+gs_fade_finish (GSFade *fade)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ if (! fade->priv->active)
+ {
+ return;
+ }
+
+ gs_fade_stop (fade);
+
+ g_signal_emit (fade, signals [FADED], 0);
+
+ fade->priv->active = FALSE;
+}
+
+static gboolean
+fade_out_timer (GSFade *fade)
+{
+ gboolean res;
+
+ res = gs_fade_out_iter (fade);
+
+ /* if failed then fade is complete */
+ if (! res)
+ {
+ gs_fade_finish (fade);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_fade_get_active (GSFade *fade)
+{
+ g_return_val_if_fail (GS_IS_FADE (fade), FALSE);
+
+ return fade->priv->active;
+}
+
+static void
+gs_fade_set_timeout (GSFade *fade,
+ guint timeout)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ fade->priv->timeout = timeout;
+}
+
+static void
+gs_fade_start (GSFade *fade,
+ guint timeout)
+{
+ guint steps_per_sec = 30;
+ guint msecs_per_step;
+ struct GSFadeScreenPrivate *screen_priv;
+ gboolean active_fade, res;
+ int i;
+
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ screen_priv = &fade->priv->screen_priv[i];
+ if (screen_priv->fade_type != FADE_TYPE_NONE)
+ {
+ res = screen_priv->fade_setup (fade, i);
+ if (res == FALSE)
+ return;
+ }
+ }
+
+ if (fade->priv->timer_id > 0)
+ {
+ gs_fade_stop (fade);
+ }
+
+ fade->priv->active = TRUE;
+
+ gs_fade_set_timeout (fade, timeout);
+
+ active_fade = FALSE;
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ screen_priv = &fade->priv->screen_priv[i];
+ if (screen_priv->fade_type != FADE_TYPE_NONE)
+ active_fade = TRUE;
+ }
+ if (active_fade)
+ {
+ guint num_steps;
+
+ num_steps = (fade->priv->timeout / 1000) * steps_per_sec;
+ msecs_per_step = 1000 / steps_per_sec;
+ fade->priv->alpha_per_iter = 1.0 / (gdouble)num_steps;
+
+ fade->priv->timer_id = g_timeout_add (msecs_per_step, (GSourceFunc)fade_out_timer, fade);
+ }
+ else
+ {
+ gs_fade_finish (fade);
+ }
+}
+
+typedef struct
+{
+ GSFadeDoneFunc done_cb;
+ gpointer data;
+} FadedCallbackData;
+
+static void
+gs_fade_async_callback (GSFade *fade,
+ FadedCallbackData *cdata)
+{
+ g_signal_handlers_disconnect_by_func (fade,
+ gs_fade_async_callback,
+ cdata);
+
+ if (cdata->done_cb)
+ {
+ cdata->done_cb (fade, cdata->data);
+ }
+
+ g_free (cdata);
+}
+
+void
+gs_fade_async (GSFade *fade,
+ guint timeout,
+ GSFadeDoneFunc func,
+ gpointer data)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ /* if fade is active then pause it */
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ if (func)
+ {
+ FadedCallbackData *cb_data;
+
+ cb_data = g_new0 (FadedCallbackData, 1);
+ cb_data->done_cb = func;
+ cb_data->data = data;
+
+ g_signal_connect (fade, "faded",
+ G_CALLBACK (gs_fade_async_callback),
+ cb_data);
+ }
+
+ gs_fade_start (fade, timeout);
+}
+
+static void
+gs_fade_sync_callback (GSFade *fade,
+ int *flag)
+{
+ *flag = TRUE;
+ g_signal_handlers_disconnect_by_func (fade,
+ gs_fade_sync_callback,
+ flag);
+}
+
+void
+gs_fade_sync (GSFade *fade,
+ guint timeout)
+{
+ int flag = FALSE;
+
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ /* if fade is active then pause it */
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ g_signal_connect (fade, "faded",
+ G_CALLBACK (gs_fade_sync_callback),
+ &flag);
+
+ gs_fade_start (fade, timeout);
+
+ while (! flag)
+ {
+ gtk_main_iteration ();
+ }
+}
+
+void
+gs_fade_reset (GSFade *fade)
+{
+ int i;
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ gs_debug ("Resetting fade");
+
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ fade->priv->current_alpha = 1.0;
+
+ gs_fade_set_alpha (fade, fade->priv->current_alpha);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ fade->priv->screen_priv[i].fade_finish (fade, i);
+}
+
+static void
+gs_fade_class_init (GSFadeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_fade_finalize;
+
+ signals [FADED] =
+ g_signal_new ("faded",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSFadeClass, faded),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0, G_TYPE_NONE);
+
+ g_type_class_add_private (klass, sizeof (GSFadePrivate));
+}
+
+static void
+gs_fade_init (GSFade *fade)
+{
+ GdkDisplay *display;
+ int i;
+
+ fade->priv = GS_FADE_GET_PRIVATE (fade);
+
+ fade->priv->timeout = 1000;
+ fade->priv->current_alpha = 1.0;
+
+ display = gdk_display_get_default ();
+ fade->priv->num_screens = gdk_display_get_n_screens (display);
+
+ fade->priv->screen_priv = g_new0 (struct GSFadeScreenPrivate, fade->priv->num_screens);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ check_randr_extension (fade, i);
+ if (!fade->priv->screen_priv[i].fade_type)
+ check_gamma_extension (fade, i);
+ gs_debug ("Fade type: %d", fade->priv->screen_priv[i].fade_type);
+ }
+}
+
+static void
+gs_fade_finalize (GObject *object)
+{
+ GSFade *fade;
+ int i;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_FADE (object));
+
+ fade = GS_FADE (object);
+
+ g_return_if_fail (fade->priv != NULL);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ fade->priv->screen_priv[i].fade_finish(fade, i);
+
+ if (fade->priv->screen_priv)
+ {
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ if (!fade->priv->screen_priv[i].rrscreen)
+ continue;
+ mate_rr_screen_destroy (fade->priv->screen_priv[i].rrscreen);
+ }
+
+ g_free (fade->priv->screen_priv);
+ fade->priv->screen_priv = NULL;
+ }
+
+ G_OBJECT_CLASS (gs_fade_parent_class)->finalize (object);
+}
+
+GSFade *
+gs_fade_new (void)
+{
+ if (fade_object)
+ {
+ g_object_ref (fade_object);
+ }
+ else
+ {
+ fade_object = g_object_new (GS_TYPE_FADE, NULL);
+ g_object_add_weak_pointer (fade_object,
+ (gpointer *) &fade_object);
+ }
+
+ return GS_FADE (fade_object);
+}
diff --git a/src/gs-fade.c.orig b/src/gs-fade.c.orig
new file mode 100644
index 0000000..3b46004
--- /dev/null
+++ b/src/gs-fade.c.orig
@@ -0,0 +1,987 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2009 William Jon McCann <[email protected]>
+ * Copyright (C) 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-fade.h"
+#include "gs-debug.h"
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+
+#include "libmateui/mate-rr.h"
+
+/* XFree86 4.x+ Gamma fading */
+
+
+#ifdef HAVE_XF86VMODE_GAMMA
+
+#include <X11/extensions/xf86vmode.h>
+
+#define XF86_MIN_GAMMA 0.1
+
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void gs_fade_class_init (GSFadeClass *klass);
+static void gs_fade_init (GSFade *fade);
+static void gs_fade_finalize (GObject *object);
+
+#define GS_FADE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_FADE, GSFadePrivate))
+
+struct GSGammaInfo
+{
+ int size;
+ unsigned short *r;
+ unsigned short *g;
+ unsigned short *b;
+};
+
+struct GSFadeScreenPrivate
+{
+ int fade_type;
+ int num_ramps;
+ /* one per crtc in randr mode */
+ struct GSGammaInfo *info;
+ /* one per screen in theory */
+ MateRRScreen *rrscreen;
+#ifdef HAVE_XF86VMODE_GAMMA
+ /* one per screen also */
+ XF86VidModeGamma vmg;
+#endif /* HAVE_XF86VMODE_GAMMA */
+ gboolean (*fade_setup) (GSFade *fade,
+ int screen);
+ gboolean (*fade_set_alpha_gamma) (GSFade *fade,
+ int screen,
+ gdouble alpha);
+ void (*fade_finish) (GSFade *fade,
+ int screen);
+};
+
+struct GSFadePrivate
+{
+ guint enabled : 1;
+ guint active : 1;
+
+ guint timeout;
+
+ guint step;
+ guint num_steps;
+ guint timer_id;
+
+ gdouble alpha_per_iter;
+ gdouble current_alpha;
+
+ int num_screens;
+
+ struct GSFadeScreenPrivate *screen_priv;
+};
+
+enum
+{
+ FADED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ FADE_TYPE_NONE,
+ FADE_TYPE_GAMMA_NUMBER,
+ FADE_TYPE_GAMMA_RAMP,
+ FADE_TYPE_XRANDR,
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSFade, gs_fade, G_TYPE_OBJECT)
+
+static gpointer fade_object = NULL;
+
+#ifdef HAVE_XF86VMODE_GAMMA
+
+/* This is needed because the VidMode extension doesn't work
+ on remote displays -- but if the remote display has the extension
+ at all, XF86VidModeQueryExtension returns true, and then
+ XF86VidModeQueryVersion dies with an X error.
+*/
+
+static gboolean error_handler_hit = FALSE;
+
+static int
+ignore_all_errors_ehandler (Display *dpy,
+ XErrorEvent *error)
+{
+ error_handler_hit = TRUE;
+
+ return 0;
+}
+
+static Bool
+safe_XF86VidModeQueryVersion (Display *dpy,
+ int *majP,
+ int *minP)
+{
+ Bool result;
+ XErrorHandler old_handler;
+
+ XSync (dpy, False);
+ error_handler_hit = FALSE;
+ old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+
+ result = XF86VidModeQueryVersion (dpy, majP, minP);
+
+ XSync (dpy, False);
+ XSetErrorHandler (old_handler);
+ XSync (dpy, False);
+
+ return (error_handler_hit
+ ? False
+ : result);
+}
+
+static gboolean
+xf86_whack_gamma (int screen,
+ struct GSFadeScreenPrivate *screen_priv,
+ float ratio)
+{
+ Bool status;
+ struct GSGammaInfo *gamma_info;
+
+ gamma_info = screen_priv->info;
+
+ if (!gamma_info)
+ return FALSE;
+
+ if (ratio < 0)
+ {
+ ratio = 0;
+ }
+ if (ratio > 1)
+ {
+ ratio = 1;
+ }
+
+ if (gamma_info->size == 0)
+ {
+ /* we only have a gamma number, not a ramp. */
+
+ XF86VidModeGamma g2;
+
+ g2.red = screen_priv->vmg.red * ratio;
+ g2.green = screen_priv->vmg.green * ratio;
+ g2.blue = screen_priv->vmg.blue * ratio;
+
+ if (g2.red < XF86_MIN_GAMMA)
+ {
+ g2.red = XF86_MIN_GAMMA;
+ }
+ if (g2.green < XF86_MIN_GAMMA)
+ {
+ g2.green = XF86_MIN_GAMMA;
+ }
+ if (g2.blue < XF86_MIN_GAMMA)
+ {
+ g2.blue = XF86_MIN_GAMMA;
+ }
+
+ status = XF86VidModeSetGamma (GDK_DISPLAY (), screen, &g2);
+ }
+ else
+ {
+
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+ unsigned short *r, *g, *b;
+ int i;
+
+ r = g_new0 (unsigned short, gamma_info->size);
+ g = g_new0 (unsigned short, gamma_info->size);
+ b = g_new0 (unsigned short, gamma_info->size);
+
+ for (i = 0; i < gamma_info->size; i++)
+ {
+ r[i] = gamma_info->r[i] * ratio;
+ g[i] = gamma_info->g[i] * ratio;
+ b[i] = gamma_info->b[i] * ratio;
+ }
+
+ status = XF86VidModeSetGammaRamp (GDK_DISPLAY (), screen, gamma_info->size, r, g, b);
+
+ g_free (r);
+ g_free (g);
+ g_free (b);
+
+# else /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ abort ();
+# endif /* !HAVE_XF86VMODE_GAMMA_RAMP */
+ }
+
+ gdk_flush ();
+
+ return status;
+}
+
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+/* VidModeExtension version 2.0 or better is needed to do gamma.
+ 2.0 added gamma values; 2.1 added gamma ramps.
+*/
+# define XF86_VIDMODE_GAMMA_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_MIN_MINOR 0
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR 2
+# define XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR 1
+
+
+gboolean
+gs_fade_get_enabled (GSFade *fade)
+{
+ g_return_val_if_fail (GS_IS_FADE (fade), FALSE);
+
+ return fade->priv->enabled;
+}
+
+void
+gs_fade_set_enabled (GSFade *fade,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ if (fade->priv->enabled != enabled)
+ {
+ fade->priv->enabled = enabled;
+ }
+}
+
+#ifdef HAVE_XF86VMODE_GAMMA
+static gboolean
+gamma_fade_setup (GSFade *fade, int screen_idx)
+{
+ gboolean res;
+ struct GSFadeScreenPrivate *screen_priv;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (screen_priv->info)
+ return TRUE;
+
+# ifndef HAVE_XF86VMODE_GAMMA_RAMP
+ if (FADE_TYPE_GAMMA_RAMP == screen_priv->fade_type)
+ {
+ /* server is newer than client! */
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ }
+# endif
+
+# ifdef HAVE_XF86VMODE_GAMMA_RAMP
+
+ screen_priv->info = g_new0(struct GSGammaInfo, 1);
+ screen_priv->num_ramps = 1;
+
+ if (FADE_TYPE_GAMMA_RAMP == screen_priv->fade_type)
+ {
+ /* have ramps */
+
+ res = XF86VidModeGetGammaRampSize (GDK_DISPLAY (), screen_idx, &screen_priv->info->size);
+ if (!res || screen_priv->info->size <= 0)
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+
+ screen_priv->info->r = g_new0 (unsigned short, screen_priv->info->size);
+ screen_priv->info->g = g_new0 (unsigned short, screen_priv->info->size);
+ screen_priv->info->b = g_new0 (unsigned short, screen_priv->info->size);
+
+ if (! (screen_priv->info->r && screen_priv->info->g && screen_priv->info->b))
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+
+ res = XF86VidModeGetGammaRamp (GDK_DISPLAY (),
+ screen_idx,
+ screen_priv->info->size,
+ screen_priv->info->r,
+ screen_priv->info->g,
+ screen_priv->info->b);
+ if (! res)
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ goto test_number;
+ }
+ gs_debug ("Initialized gamma ramp fade");
+ }
+# endif /* HAVE_XF86VMODE_GAMMA_RAMP */
+
+test_number:
+ if (FADE_TYPE_GAMMA_NUMBER == screen_priv->fade_type)
+ {
+ /* only have gamma parameter, not ramps. */
+
+ res = XF86VidModeGetGamma (GDK_DISPLAY (), screen_idx, &screen_priv->vmg);
+ if (! res)
+ {
+ screen_priv->fade_type = FADE_TYPE_NONE;
+ goto test_none;
+ }
+ gs_debug ("Initialized gamma fade for screen %d: %f %f %f",
+ screen_idx,
+ screen_priv->vmg.red,
+ screen_priv->vmg.green,
+ screen_priv->vmg.blue);
+ }
+
+test_none:
+ if (FADE_TYPE_NONE == screen_priv->fade_type)
+ {
+ goto FAIL;
+ }
+
+ return TRUE;
+FAIL:
+
+ return FALSE;
+}
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void
+screen_fade_finish (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ int i;
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (!screen_priv->info)
+ return;
+
+ for (i = 0; i < screen_priv->num_ramps; i++)
+ {
+ if (screen_priv->info[i].r)
+ g_free (screen_priv->info[i].r);
+ if (screen_priv->info[i].g)
+ g_free (screen_priv->info[i].g);
+ if (screen_priv->info[i].b)
+ g_free (screen_priv->info[i].b);
+ }
+
+ g_free (screen_priv->info);
+ screen_priv->info = NULL;
+ screen_priv->num_ramps = 0;
+}
+
+#ifdef HAVE_XF86VMODE_GAMMA
+static gboolean
+gamma_fade_set_alpha_gamma (GSFade *fade,
+ int screen_idx,
+ gdouble alpha)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ gboolean res;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+ res = xf86_whack_gamma (screen_idx, screen_priv, alpha);
+
+ return TRUE;
+}
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+static void
+check_gamma_extension (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+#ifdef HAVE_XF86VMODE_GAMMA
+ int event;
+ int error;
+ int major;
+ int minor;
+ gboolean res;
+#endif /* HAVE_XF86VMODE_GAMMA */
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+#ifdef HAVE_XF86VMODE_GAMMA
+ res = XF86VidModeQueryExtension (GDK_DISPLAY (), &event, &error);
+ if (! res)
+ goto fade_none;
+
+ res = safe_XF86VidModeQueryVersion (GDK_DISPLAY (), &major, &minor);
+ if (! res)
+ goto fade_none;
+
+ if (major < XF86_VIDMODE_GAMMA_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_MIN_MINOR))
+ goto fade_none;
+
+ screen_priv->fade_setup = gamma_fade_setup;
+ screen_priv->fade_finish = screen_fade_finish;
+ screen_priv->fade_set_alpha_gamma = gamma_fade_set_alpha_gamma;
+
+ if (major < XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR ||
+ (major == XF86_VIDMODE_GAMMA_RAMP_MIN_MAJOR &&
+ minor < XF86_VIDMODE_GAMMA_RAMP_MIN_MINOR))
+ {
+ screen_priv->fade_type = FADE_TYPE_GAMMA_NUMBER;
+ return;
+ }
+
+ /* Copacetic */
+ screen_priv->fade_type = FADE_TYPE_GAMMA_RAMP;
+ return;
+fade_none:
+#endif
+ screen_priv->fade_type = FADE_TYPE_NONE;
+}
+
+/* Xrandr support */
+
+static gboolean xrandr_fade_setup (GSFade *fade, int screen_idx)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ MateRRCrtc *crtc;
+ MateRRCrtc **crtcs;
+ int crtc_count = 0;
+ struct GSGammaInfo *info;
+ gboolean res;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (screen_priv->info)
+ return TRUE;
+
+ /* refresh the screen info */
+ mate_rr_screen_refresh (screen_priv->rrscreen, NULL);
+
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ while (*crtcs)
+ {
+ crtc_count++;
+ crtcs++;
+ };
+
+ screen_priv->info = g_new0 (struct GSGammaInfo, crtc_count);
+ screen_priv->num_ramps = crtc_count;
+
+ crtc_count = 0;
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ while (*crtcs)
+ {
+ crtc = *crtcs;
+
+ info = &screen_priv->info[crtc_count];
+
+ /* if no mode ignore crtc */
+ if (!mate_rr_crtc_get_current_mode (crtc))
+ {
+ info->size = 0;
+ info->r = NULL;
+ info->g = NULL;
+ info->b = NULL;
+ }
+ else
+ {
+ res = mate_rr_crtc_get_gamma (crtc, &info->size,
+ &info->r, &info->g,
+ &info->b);
+ if (res == FALSE)
+ goto fail;
+ }
+
+ crtcs++;
+ crtc_count++;
+ }
+ return TRUE;
+fail:
+ return FALSE;
+}
+
+static void xrandr_crtc_whack_gamma (MateRRCrtc *crtc,
+ struct GSGammaInfo *gamma_info,
+ float ratio)
+{
+ unsigned short *r, *g, *b;
+ int i;
+
+ if (gamma_info->size == 0)
+ return;
+
+ if (ratio < 0)
+ {
+ ratio = 0;
+ }
+ if (ratio > 1)
+ {
+ ratio = 1;
+ }
+
+ r = g_new0 (unsigned short, gamma_info->size);
+ g = g_new0 (unsigned short, gamma_info->size);
+ b = g_new0 (unsigned short, gamma_info->size);
+
+ for (i = 0; i < gamma_info->size; i++)
+ {
+ r[i] = gamma_info->r[i] * ratio;
+ g[i] = gamma_info->g[i] * ratio;
+ b[i] = gamma_info->b[i] * ratio;
+ }
+
+ mate_rr_crtc_set_gamma (crtc, gamma_info->size,
+ r, g, b);
+ g_free (r);
+ g_free (g);
+ g_free (b);
+}
+
+static gboolean xrandr_fade_set_alpha_gamma (GSFade *fade,
+ int screen_idx,
+ gdouble alpha)
+{
+ struct GSFadeScreenPrivate *screen_priv;
+ struct GSGammaInfo *info;
+ MateRRCrtc **crtcs;
+ int i;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ if (!screen_priv->info)
+ return FALSE;
+
+ crtcs = mate_rr_screen_list_crtcs (screen_priv->rrscreen);
+ i = 0;
+
+ while (*crtcs)
+ {
+ info = &screen_priv->info[i];
+ xrandr_crtc_whack_gamma (*crtcs, info, alpha);
+ i++;
+ crtcs++;
+ }
+ return TRUE;
+}
+
+static void
+check_randr_extension (GSFade *fade, int screen_idx)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ GdkScreen *screen = gdk_display_get_screen (display, screen_idx);
+ struct GSFadeScreenPrivate *screen_priv;
+
+ screen_priv = &fade->priv->screen_priv[screen_idx];
+
+ screen_priv->rrscreen = mate_rr_screen_new (screen,
+ NULL,
+ NULL,
+ NULL);
+ if (!screen_priv->rrscreen)
+ {
+ screen_priv->fade_type = FADE_TYPE_NONE;
+ return;
+ }
+
+ screen_priv->fade_type = FADE_TYPE_XRANDR;
+ screen_priv->fade_setup = xrandr_fade_setup;
+ screen_priv->fade_finish = screen_fade_finish;
+ screen_priv->fade_set_alpha_gamma = xrandr_fade_set_alpha_gamma;
+}
+
+static gboolean
+gs_fade_set_alpha (GSFade *fade,
+ gdouble alpha)
+{
+ gboolean ret = FALSE;
+ int i;
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ switch (fade->priv->screen_priv[i].fade_type)
+ {
+ case FADE_TYPE_GAMMA_RAMP:
+ case FADE_TYPE_GAMMA_NUMBER:
+ case FADE_TYPE_XRANDR:
+ ret = fade->priv->screen_priv[i].fade_set_alpha_gamma (fade, i, alpha);
+ break;
+ case FADE_TYPE_NONE:
+ ret = FALSE;
+ break;
+ default:
+ g_warning ("Unknown fade type");
+ ret = FALSE;
+ break;
+ }
+ }
+ return ret;
+}
+
+static gboolean
+gs_fade_out_iter (GSFade *fade)
+{
+ gboolean ret;
+
+ if (fade->priv->current_alpha < 0.01)
+ {
+ return FALSE;
+ }
+
+ fade->priv->current_alpha -= fade->priv->alpha_per_iter;
+
+ ret = gs_fade_set_alpha (fade, fade->priv->current_alpha);
+
+ return ret;
+}
+
+static gboolean
+gs_fade_stop (GSFade *fade)
+{
+ if (fade->priv->timer_id > 0)
+ {
+ g_source_remove (fade->priv->timer_id);
+ fade->priv->timer_id = 0;
+ }
+
+ fade->priv->step = 0;
+ fade->priv->active = FALSE;
+
+ return TRUE;
+}
+
+void
+gs_fade_finish (GSFade *fade)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ if (! fade->priv->active)
+ {
+ return;
+ }
+
+ gs_fade_stop (fade);
+
+ g_signal_emit (fade, signals [FADED], 0);
+
+ fade->priv->active = FALSE;
+}
+
+static gboolean
+fade_out_timer (GSFade *fade)
+{
+ gboolean res;
+
+ res = gs_fade_out_iter (fade);
+
+ /* if failed then fade is complete */
+ if (! res)
+ {
+ gs_fade_finish (fade);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_fade_get_active (GSFade *fade)
+{
+ g_return_val_if_fail (GS_IS_FADE (fade), FALSE);
+
+ return fade->priv->active;
+}
+
+static void
+gs_fade_set_timeout (GSFade *fade,
+ guint timeout)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ fade->priv->timeout = timeout;
+}
+
+static void
+gs_fade_start (GSFade *fade,
+ guint timeout)
+{
+ guint steps_per_sec = 30;
+ guint msecs_per_step;
+ struct GSFadeScreenPrivate *screen_priv;
+ gboolean active_fade, res;
+ int i;
+
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ screen_priv = &fade->priv->screen_priv[i];
+ if (screen_priv->fade_type != FADE_TYPE_NONE)
+ {
+ res = screen_priv->fade_setup (fade, i);
+ if (res == FALSE)
+ return;
+ }
+ }
+
+ if (fade->priv->timer_id > 0)
+ {
+ gs_fade_stop (fade);
+ }
+
+ fade->priv->active = TRUE;
+
+ gs_fade_set_timeout (fade, timeout);
+
+ active_fade = FALSE;
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ screen_priv = &fade->priv->screen_priv[i];
+ if (screen_priv->fade_type != FADE_TYPE_NONE)
+ active_fade = TRUE;
+ }
+ if (active_fade)
+ {
+ guint num_steps;
+
+ num_steps = (fade->priv->timeout / 1000) * steps_per_sec;
+ msecs_per_step = 1000 / steps_per_sec;
+ fade->priv->alpha_per_iter = 1.0 / (gdouble)num_steps;
+
+ fade->priv->timer_id = g_timeout_add (msecs_per_step, (GSourceFunc)fade_out_timer, fade);
+ }
+ else
+ {
+ gs_fade_finish (fade);
+ }
+}
+
+typedef struct
+{
+ GSFadeDoneFunc done_cb;
+ gpointer data;
+} FadedCallbackData;
+
+static void
+gs_fade_async_callback (GSFade *fade,
+ FadedCallbackData *cdata)
+{
+ g_signal_handlers_disconnect_by_func (fade,
+ gs_fade_async_callback,
+ cdata);
+
+ if (cdata->done_cb)
+ {
+ cdata->done_cb (fade, cdata->data);
+ }
+
+ g_free (cdata);
+}
+
+void
+gs_fade_async (GSFade *fade,
+ guint timeout,
+ GSFadeDoneFunc func,
+ gpointer data)
+{
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ /* if fade is active then pause it */
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ if (func)
+ {
+ FadedCallbackData *cb_data;
+
+ cb_data = g_new0 (FadedCallbackData, 1);
+ cb_data->done_cb = func;
+ cb_data->data = data;
+
+ g_signal_connect (fade, "faded",
+ G_CALLBACK (gs_fade_async_callback),
+ cb_data);
+ }
+
+ gs_fade_start (fade, timeout);
+}
+
+static void
+gs_fade_sync_callback (GSFade *fade,
+ int *flag)
+{
+ *flag = TRUE;
+ g_signal_handlers_disconnect_by_func (fade,
+ gs_fade_sync_callback,
+ flag);
+}
+
+void
+gs_fade_sync (GSFade *fade,
+ guint timeout)
+{
+ int flag = FALSE;
+
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ /* if fade is active then pause it */
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ g_signal_connect (fade, "faded",
+ G_CALLBACK (gs_fade_sync_callback),
+ &flag);
+
+ gs_fade_start (fade, timeout);
+
+ while (! flag)
+ {
+ gtk_main_iteration ();
+ }
+}
+
+void
+gs_fade_reset (GSFade *fade)
+{
+ int i;
+ g_return_if_fail (GS_IS_FADE (fade));
+
+ gs_debug ("Resetting fade");
+
+ if (fade->priv->active)
+ {
+ gs_fade_stop (fade);
+ }
+
+ fade->priv->current_alpha = 1.0;
+
+ gs_fade_set_alpha (fade, fade->priv->current_alpha);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ fade->priv->screen_priv[i].fade_finish (fade, i);
+}
+
+static void
+gs_fade_class_init (GSFadeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_fade_finalize;
+
+ signals [FADED] =
+ g_signal_new ("faded",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSFadeClass, faded),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0, G_TYPE_NONE);
+
+ g_type_class_add_private (klass, sizeof (GSFadePrivate));
+}
+
+static void
+gs_fade_init (GSFade *fade)
+{
+ GdkDisplay *display;
+ int i;
+
+ fade->priv = GS_FADE_GET_PRIVATE (fade);
+
+ fade->priv->timeout = 1000;
+ fade->priv->current_alpha = 1.0;
+
+ display = gdk_display_get_default ();
+ fade->priv->num_screens = gdk_display_get_n_screens (display);
+
+ fade->priv->screen_priv = g_new0 (struct GSFadeScreenPrivate, fade->priv->num_screens);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ check_randr_extension (fade, i);
+ if (!fade->priv->screen_priv[i].fade_type)
+ check_gamma_extension (fade, i);
+ gs_debug ("Fade type: %d", fade->priv->screen_priv[i].fade_type);
+ }
+}
+
+static void
+gs_fade_finalize (GObject *object)
+{
+ GSFade *fade;
+ int i;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_FADE (object));
+
+ fade = GS_FADE (object);
+
+ g_return_if_fail (fade->priv != NULL);
+
+ for (i = 0; i < fade->priv->num_screens; i++)
+ fade->priv->screen_priv[i].fade_finish(fade, i);
+
+ if (fade->priv->screen_priv)
+ {
+ for (i = 0; i < fade->priv->num_screens; i++)
+ {
+ if (!fade->priv->screen_priv[i].rrscreen)
+ continue;
+ mate_rr_screen_destroy (fade->priv->screen_priv[i].rrscreen);
+ }
+
+ g_free (fade->priv->screen_priv);
+ fade->priv->screen_priv = NULL;
+ }
+
+ G_OBJECT_CLASS (gs_fade_parent_class)->finalize (object);
+}
+
+GSFade *
+gs_fade_new (void)
+{
+ if (fade_object)
+ {
+ g_object_ref (fade_object);
+ }
+ else
+ {
+ fade_object = g_object_new (GS_TYPE_FADE, NULL);
+ g_object_add_weak_pointer (fade_object,
+ (gpointer *) &fade_object);
+ }
+
+ return GS_FADE (fade_object);
+}
diff --git a/src/gs-fade.h b/src/gs-fade.h
new file mode 100644
index 0000000..65f3e7f
--- /dev/null
+++ b/src/gs-fade.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_FADE_H
+#define __GS_FADE_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_FADE (gs_fade_get_type ())
+#define GS_FADE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_FADE, GSFade))
+#define GS_FADE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_FADE, GSFadeClass))
+#define GS_IS_FADE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_FADE))
+#define GS_IS_FADE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_FADE))
+#define GS_FADE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_FADE, GSFadeClass))
+
+
+typedef struct GSFadePrivate GSFadePrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSFadePrivate *priv;
+} GSFade;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* faded) (GSFade *fade);
+} GSFadeClass;
+
+typedef void (* GSFadeDoneFunc) (GSFade *fade,
+ gpointer data);
+
+
+GType gs_fade_get_type (void);
+
+GSFade * gs_fade_new (void);
+
+void gs_fade_async (GSFade *fade,
+ guint timeout,
+ GSFadeDoneFunc done_cb,
+ gpointer data);
+void gs_fade_sync (GSFade *fade,
+ guint timeout);
+
+void gs_fade_finish (GSFade *fade);
+void gs_fade_reset (GSFade *fade);
+
+gboolean gs_fade_get_active (GSFade *fade);
+
+gboolean gs_fade_get_enabled (GSFade *fade);
+void gs_fade_set_enabled (GSFade *fade,
+ gboolean enabled);
+
+G_END_DECLS
+
+#endif /* __GS_FADE_H */
diff --git a/src/gs-fade.h.orig b/src/gs-fade.h.orig
new file mode 100644
index 0000000..bfa7c2a
--- /dev/null
+++ b/src/gs-fade.h.orig
@@ -0,0 +1,77 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_FADE_H
+#define __GS_FADE_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_FADE (gs_fade_get_type ())
+#define GS_FADE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_FADE, GSFade))
+#define GS_FADE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_FADE, GSFadeClass))
+#define GS_IS_FADE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_FADE))
+#define GS_IS_FADE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_FADE))
+#define GS_FADE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_FADE, GSFadeClass))
+
+
+typedef struct GSFadePrivate GSFadePrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSFadePrivate *priv;
+} GSFade;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* faded) (GSFade *fade);
+} GSFadeClass;
+
+typedef void (* GSFadeDoneFunc) (GSFade *fade,
+ gpointer data);
+
+
+GType gs_fade_get_type (void);
+
+GSFade * gs_fade_new (void);
+
+void gs_fade_async (GSFade *fade,
+ guint timeout,
+ GSFadeDoneFunc done_cb,
+ gpointer data);
+void gs_fade_sync (GSFade *fade,
+ guint timeout);
+
+void gs_fade_finish (GSFade *fade);
+void gs_fade_reset (GSFade *fade);
+
+gboolean gs_fade_get_active (GSFade *fade);
+
+gboolean gs_fade_get_enabled (GSFade *fade);
+void gs_fade_set_enabled (GSFade *fade,
+ gboolean enabled);
+
+G_END_DECLS
+
+#endif /* __GS_FADE_H */
diff --git a/src/gs-grab-x11.c b/src/gs-grab-x11.c
new file mode 100644
index 0000000..9eca1ec
--- /dev/null
+++ b/src/gs-grab-x11.c
@@ -0,0 +1,672 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+# include <X11/extensions/xf86misc.h>
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
+#include "gs-window.h"
+#include "gs-grab.h"
+#include "gs-debug.h"
+
+static void gs_grab_class_init (GSGrabClass *klass);
+static void gs_grab_init (GSGrab *grab);
+static void gs_grab_finalize (GObject *object);
+
+#define GS_GRAB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_GRAB, GSGrabPrivate))
+
+G_DEFINE_TYPE (GSGrab, gs_grab, G_TYPE_OBJECT)
+
+static gpointer grab_object = NULL;
+
+struct GSGrabPrivate
+{
+ guint mouse_hide_cursor : 1;
+ GdkWindow *mouse_grab_window;
+ GdkWindow *keyboard_grab_window;
+ GdkScreen *mouse_grab_screen;
+ GdkScreen *keyboard_grab_screen;
+
+ GtkWidget *invisible;
+};
+
+static GdkCursor *
+get_cursor (void)
+{
+ GdkBitmap *empty_bitmap;
+ GdkCursor *cursor;
+ GdkColor useless;
+ char invisible_cursor_bits [] = { 0x0 };
+
+ useless.red = useless.green = useless.blue = 0;
+ useless.pixel = 0;
+
+ empty_bitmap = gdk_bitmap_create_from_data (NULL,
+ invisible_cursor_bits,
+ 1, 1);
+
+ cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
+ empty_bitmap,
+ &useless,
+ &useless, 0, 0);
+
+ g_object_unref (empty_bitmap);
+
+ return cursor;
+}
+
+static const char *
+grab_string (int status)
+{
+ switch (status)
+ {
+ case GDK_GRAB_SUCCESS:
+ return "GrabSuccess";
+ case GDK_GRAB_ALREADY_GRABBED:
+ return "AlreadyGrabbed";
+ case GDK_GRAB_INVALID_TIME:
+ return "GrabInvalidTime";
+ case GDK_GRAB_NOT_VIEWABLE:
+ return "GrabNotViewable";
+ case GDK_GRAB_FROZEN:
+ return "GrabFrozen";
+ default:
+ {
+ static char foo [255];
+ sprintf (foo, "unknown status: %d", status);
+ return foo;
+ }
+ }
+}
+
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+/* This function enables and disables the Ctrl-Alt-KP_star and
+ Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
+ grabs and/or kill the grabbing client. That would effectively
+ unlock the screen, so we don't like that.
+
+ The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
+ if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
+ in XF86Config. I believe they are disabled by default.
+
+ This does not affect any other keys (specifically Ctrl-Alt-BS or
+ Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
+ */
+static void
+xorg_lock_smasher_set_active (GSGrab *grab,
+ gboolean active)
+{
+ int status, event, error;
+
+ if (!XF86MiscQueryExtension (GDK_DISPLAY (), &event, &error))
+ {
+ gs_debug ("No XFree86-Misc extension present");
+ return;
+ }
+
+ if (active)
+ {
+ gs_debug ("Enabling the x.org grab smasher");
+ }
+ else
+ {
+ gs_debug ("Disabling the x.org grab smasher");
+ }
+
+ gdk_error_trap_push ();
+
+ status = XF86MiscSetGrabKeysState (GDK_DISPLAY (), active);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+
+ if (active && status == MiscExtGrabStateAlready)
+ {
+ /* shut up, consider this success */
+ status = MiscExtGrabStateSuccess;
+ }
+
+ gs_debug ("XF86MiscSetGrabKeysState(%s) returned %s\n",
+ active ? "on" : "off",
+ (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
+ status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
+ status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
+ "unknown value"));
+}
+#else
+static void
+xorg_lock_smasher_set_active (GSGrab *grab,
+ gboolean active)
+{
+}
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
+static int
+gs_grab_get_keyboard (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen)
+{
+ GdkGrabStatus status;
+
+ g_return_val_if_fail (window != NULL, FALSE);
+ g_return_val_if_fail (screen != NULL, FALSE);
+
+ gs_debug ("Grabbing keyboard widget=%X", (guint32) GDK_WINDOW_XID (window));
+ status = gdk_keyboard_grab (window, FALSE, GDK_CURRENT_TIME);
+
+ if (status == GDK_GRAB_SUCCESS)
+ {
+ if (grab->priv->keyboard_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
+ (gpointer *) &grab->priv->keyboard_grab_window);
+ }
+ grab->priv->keyboard_grab_window = window;
+
+ g_object_add_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
+ (gpointer *) &grab->priv->keyboard_grab_window);
+
+ grab->priv->keyboard_grab_screen = screen;
+ }
+ else
+ {
+ gs_debug ("Couldn't grab keyboard! (%s)", grab_string (status));
+ }
+
+ return status;
+}
+
+static int
+gs_grab_get_mouse (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ GdkGrabStatus status;
+ GdkCursor *cursor;
+
+ g_return_val_if_fail (window != NULL, FALSE);
+ g_return_val_if_fail (screen != NULL, FALSE);
+
+ cursor = get_cursor ();
+
+ gs_debug ("Grabbing mouse widget=%X", (guint32) GDK_WINDOW_XID (window));
+ status = gdk_pointer_grab (window, TRUE, 0, NULL,
+ (hide_cursor ? cursor : NULL),
+ GDK_CURRENT_TIME);
+
+ if (status == GDK_GRAB_SUCCESS)
+ {
+ if (grab->priv->mouse_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window),
+ (gpointer *) &grab->priv->mouse_grab_window);
+ }
+ grab->priv->mouse_grab_window = window;
+
+ g_object_add_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window),
+ (gpointer *) &grab->priv->mouse_grab_window);
+
+ grab->priv->mouse_grab_screen = screen;
+ grab->priv->mouse_hide_cursor = hide_cursor;
+ }
+
+ gdk_cursor_unref (cursor);
+
+ return status;
+}
+
+void
+gs_grab_keyboard_reset (GSGrab *grab)
+{
+ if (grab->priv->keyboard_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
+ (gpointer *) &grab->priv->keyboard_grab_window);
+ }
+ grab->priv->keyboard_grab_window = NULL;
+ grab->priv->keyboard_grab_screen = NULL;
+}
+
+static gboolean
+gs_grab_release_keyboard (GSGrab *grab)
+{
+ gs_debug ("Ungrabbing keyboard");
+ gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+
+ gs_grab_keyboard_reset (grab);
+
+ return TRUE;
+}
+
+void
+gs_grab_mouse_reset (GSGrab *grab)
+{
+ if (grab->priv->mouse_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window),
+ (gpointer *) &grab->priv->mouse_grab_window);
+ }
+
+ grab->priv->mouse_grab_window = NULL;
+ grab->priv->mouse_grab_screen = NULL;
+}
+
+gboolean
+gs_grab_release_mouse (GSGrab *grab)
+{
+ gs_debug ("Ungrabbing pointer");
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+
+ gs_grab_mouse_reset (grab);
+
+ return TRUE;
+}
+
+static gboolean
+gs_grab_move_mouse (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ gboolean result;
+ GdkWindow *old_window;
+ GdkScreen *old_screen;
+ gboolean old_hide_cursor;
+
+ /* if the pointer is not grabbed and we have a
+ mouse_grab_window defined then we lost the grab */
+ if (! gdk_pointer_is_grabbed ())
+ {
+ gs_grab_mouse_reset (grab);
+ }
+
+ if (grab->priv->mouse_grab_window == window)
+ {
+ gs_debug ("Window %X is already grabbed, skipping",
+ (guint32) GDK_WINDOW_XID (grab->priv->mouse_grab_window));
+ return TRUE;
+ }
+
+#if 0
+ gs_debug ("Intentionally skipping move pointer grabs");
+ /* FIXME: GTK doesn't like having the pointer grabbed */
+ return TRUE;
+#else
+ if (grab->priv->mouse_grab_window)
+ {
+ gs_debug ("Moving pointer grab from %X to %X",
+ (guint32) GDK_WINDOW_XID (grab->priv->mouse_grab_window),
+ (guint32) GDK_WINDOW_XID (window));
+ }
+ else
+ {
+ gs_debug ("Getting pointer grab on %X",
+ (guint32) GDK_WINDOW_XID (window));
+ }
+#endif
+
+ gs_debug ("*** doing X server grab");
+ gdk_x11_grab_server ();
+
+ old_window = grab->priv->mouse_grab_window;
+ old_screen = grab->priv->mouse_grab_screen;
+ old_hide_cursor = grab->priv->mouse_hide_cursor;
+
+ if (old_window)
+ {
+ gs_grab_release_mouse (grab);
+ }
+
+ result = gs_grab_get_mouse (grab, window, screen, hide_cursor);
+
+ if (result != GDK_GRAB_SUCCESS)
+ {
+ sleep (1);
+ result = gs_grab_get_mouse (grab, window, screen, hide_cursor);
+ }
+
+ if ((result != GDK_GRAB_SUCCESS) && old_window)
+ {
+ gs_debug ("Could not grab mouse for new window. Resuming previous grab.");
+ gs_grab_get_mouse (grab, old_window, old_screen, old_hide_cursor);
+ }
+
+ gs_debug ("*** releasing X server grab");
+ gdk_x11_ungrab_server ();
+ gdk_flush ();
+
+ return (result == GDK_GRAB_SUCCESS);
+}
+
+static gboolean
+gs_grab_move_keyboard (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen)
+{
+ gboolean result;
+ GdkWindow *old_window;
+ GdkScreen *old_screen;
+
+ if (grab->priv->keyboard_grab_window == window)
+ {
+ gs_debug ("Window %X is already grabbed, skipping",
+ (guint32) GDK_WINDOW_XID (grab->priv->keyboard_grab_window));
+ return TRUE;
+ }
+
+ if (grab->priv->keyboard_grab_window != NULL)
+ {
+ gs_debug ("Moving keyboard grab from %X to %X",
+ (guint32) GDK_WINDOW_XID (grab->priv->keyboard_grab_window),
+ (guint32) GDK_WINDOW_XID (window));
+ }
+ else
+ {
+ gs_debug ("Getting keyboard grab on %X",
+ (guint32) GDK_WINDOW_XID (window));
+
+ }
+
+ gs_debug ("*** doing X server grab");
+ gdk_x11_grab_server ();
+
+ old_window = grab->priv->keyboard_grab_window;
+ old_screen = grab->priv->keyboard_grab_screen;
+
+ if (old_window)
+ {
+ gs_grab_release_keyboard (grab);
+ }
+
+ result = gs_grab_get_keyboard (grab, window, screen);
+
+ if (result != GDK_GRAB_SUCCESS)
+ {
+ sleep (1);
+ result = gs_grab_get_keyboard (grab, window, screen);
+ }
+
+ if ((result != GDK_GRAB_SUCCESS) && old_window)
+ {
+ gs_debug ("Could not grab keyboard for new window. Resuming previous grab.");
+ gs_grab_get_keyboard (grab, old_window, old_screen);
+ }
+
+ gs_debug ("*** releasing X server grab");
+ gdk_x11_ungrab_server ();
+ gdk_flush ();
+
+ return (result == GDK_GRAB_SUCCESS);
+}
+
+static void
+gs_grab_nuke_focus (void)
+{
+ Window focus = 0;
+ int rev = 0;
+
+ gs_debug ("Nuking focus");
+
+ gdk_error_trap_push ();
+
+ XGetInputFocus (GDK_DISPLAY (), &focus, &rev);
+
+ XSetInputFocus (GDK_DISPLAY (), None, RevertToNone, CurrentTime);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+}
+
+void
+gs_grab_release (GSGrab *grab)
+{
+ gs_debug ("Releasing all grabs");
+
+ gs_grab_release_mouse (grab);
+ gs_grab_release_keyboard (grab);
+
+ /* FIXME: is it right to enable this ? */
+ xorg_lock_smasher_set_active (grab, TRUE);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_flush ();
+}
+
+gboolean
+gs_grab_grab_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ gboolean mstatus = FALSE;
+ gboolean kstatus = FALSE;
+ int i;
+ int retries = 4;
+ gboolean focus_fuckus = FALSE;
+
+AGAIN:
+
+ for (i = 0; i < retries; i++)
+ {
+ kstatus = gs_grab_get_keyboard (grab, window, screen);
+ if (kstatus == GDK_GRAB_SUCCESS)
+ {
+ break;
+ }
+
+ /* else, wait a second and try to grab again. */
+ sleep (1);
+ }
+
+ if (kstatus != GDK_GRAB_SUCCESS)
+ {
+ if (!focus_fuckus)
+ {
+ focus_fuckus = TRUE;
+ gs_grab_nuke_focus ();
+ goto AGAIN;
+ }
+ }
+
+ for (i = 0; i < retries; i++)
+ {
+ mstatus = gs_grab_get_mouse (grab, window, screen, hide_cursor);
+ if (mstatus == GDK_GRAB_SUCCESS)
+ {
+ break;
+ }
+
+ /* else, wait a second and try to grab again. */
+ sleep (1);
+ }
+
+ if (mstatus != GDK_GRAB_SUCCESS)
+ {
+ gs_debug ("Couldn't grab pointer! (%s)",
+ grab_string (mstatus));
+ }
+
+#if 0
+ /* FIXME: release the pointer grab so GTK will work */
+ gs_grab_release_mouse (grab);
+#endif
+
+ /* When should we allow blanking to proceed? The current theory
+ is that both a keyboard grab and a mouse grab are mandatory
+
+ - If we don't have a keyboard grab, then we won't be able to
+ read a password to unlock, so the kbd grab is manditory.
+
+ - If we don't have a mouse grab, then we might not see mouse
+ clicks as a signal to unblank, on-screen widgets won't work ideally,
+ and gs_grab_move_to_window() will spin forever when it gets called.
+ */
+
+ if (kstatus != GDK_GRAB_SUCCESS || mstatus != GDK_GRAB_SUCCESS)
+ {
+ /* Do not blank without a keyboard and mouse grabs. */
+
+ /* Release keyboard or mouse which was grabbed. */
+ if (kstatus == GDK_GRAB_SUCCESS)
+ {
+ gs_grab_release_keyboard (grab);
+ }
+ if (mstatus == GDK_GRAB_SUCCESS)
+ {
+ gs_grab_release_mouse (grab);
+ }
+
+ return FALSE;
+ }
+
+ /* Grab is good, go ahead and blank. */
+ return TRUE;
+}
+
+/* this is used to grab the keyboard and mouse to the root */
+gboolean
+gs_grab_grab_root (GSGrab *grab,
+ gboolean hide_cursor)
+{
+ GdkDisplay *display;
+ GdkWindow *root;
+ GdkScreen *screen;
+ gboolean res;
+
+ gs_debug ("Grabbing the root window");
+
+ display = gdk_display_get_default ();
+ gdk_display_get_pointer (display, &screen, NULL, NULL, NULL);
+ root = gdk_screen_get_root_window (screen);
+
+ res = gs_grab_grab_window (grab, root, screen, hide_cursor);
+
+ return res;
+}
+
+/* this is used to grab the keyboard and mouse to an offscreen window */
+gboolean
+gs_grab_grab_offscreen (GSGrab *grab,
+ gboolean hide_cursor)
+{
+ GdkScreen *screen;
+ gboolean res;
+
+ gs_debug ("Grabbing an offscreen window");
+
+ screen = gtk_invisible_get_screen (GTK_INVISIBLE (grab->priv->invisible));
+ res = gs_grab_grab_window (grab, grab->priv->invisible->window, screen, hide_cursor);
+
+ return res;
+}
+
+/* This is similar to gs_grab_grab_window but doesn't fail */
+void
+gs_grab_move_to_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ gboolean result = FALSE;
+
+ g_return_if_fail (GS_IS_GRAB (grab));
+
+ xorg_lock_smasher_set_active (grab, FALSE);
+
+ do
+ {
+ result = gs_grab_move_keyboard (grab, window, screen);
+ gdk_flush ();
+ }
+ while (!result);
+
+ do
+ {
+ result = gs_grab_move_mouse (grab, window, screen, hide_cursor);
+ gdk_flush ();
+ }
+ while (!result);
+}
+
+static void
+gs_grab_class_init (GSGrabClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_grab_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSGrabPrivate));
+}
+
+static void
+gs_grab_init (GSGrab *grab)
+{
+ grab->priv = GS_GRAB_GET_PRIVATE (grab);
+
+ grab->priv->mouse_hide_cursor = FALSE;
+ grab->priv->invisible = gtk_invisible_new ();
+ gtk_widget_show (grab->priv->invisible);
+}
+
+static void
+gs_grab_finalize (GObject *object)
+{
+ GSGrab *grab;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_GRAB (object));
+
+ grab = GS_GRAB (object);
+
+ g_return_if_fail (grab->priv != NULL);
+
+ gtk_widget_destroy (grab->priv->invisible);
+
+ G_OBJECT_CLASS (gs_grab_parent_class)->finalize (object);
+}
+
+GSGrab *
+gs_grab_new (void)
+{
+ if (grab_object)
+ {
+ g_object_ref (grab_object);
+ }
+ else
+ {
+ grab_object = g_object_new (GS_TYPE_GRAB, NULL);
+ g_object_add_weak_pointer (grab_object,
+ (gpointer *) &grab_object);
+ }
+
+ return GS_GRAB (grab_object);
+}
diff --git a/src/gs-grab-x11.c.orig b/src/gs-grab-x11.c.orig
new file mode 100644
index 0000000..e6e7f22
--- /dev/null
+++ b/src/gs-grab-x11.c.orig
@@ -0,0 +1,672 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+# include <X11/extensions/xf86misc.h>
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
+#include "gs-window.h"
+#include "gs-grab.h"
+#include "gs-debug.h"
+
+static void gs_grab_class_init (GSGrabClass *klass);
+static void gs_grab_init (GSGrab *grab);
+static void gs_grab_finalize (GObject *object);
+
+#define GS_GRAB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_GRAB, GSGrabPrivate))
+
+G_DEFINE_TYPE (GSGrab, gs_grab, G_TYPE_OBJECT)
+
+static gpointer grab_object = NULL;
+
+struct GSGrabPrivate
+{
+ guint mouse_hide_cursor : 1;
+ GdkWindow *mouse_grab_window;
+ GdkWindow *keyboard_grab_window;
+ GdkScreen *mouse_grab_screen;
+ GdkScreen *keyboard_grab_screen;
+
+ GtkWidget *invisible;
+};
+
+static GdkCursor *
+get_cursor (void)
+{
+ GdkBitmap *empty_bitmap;
+ GdkCursor *cursor;
+ GdkColor useless;
+ char invisible_cursor_bits [] = { 0x0 };
+
+ useless.red = useless.green = useless.blue = 0;
+ useless.pixel = 0;
+
+ empty_bitmap = gdk_bitmap_create_from_data (NULL,
+ invisible_cursor_bits,
+ 1, 1);
+
+ cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
+ empty_bitmap,
+ &useless,
+ &useless, 0, 0);
+
+ g_object_unref (empty_bitmap);
+
+ return cursor;
+}
+
+static const char *
+grab_string (int status)
+{
+ switch (status)
+ {
+ case GDK_GRAB_SUCCESS:
+ return "GrabSuccess";
+ case GDK_GRAB_ALREADY_GRABBED:
+ return "AlreadyGrabbed";
+ case GDK_GRAB_INVALID_TIME:
+ return "GrabInvalidTime";
+ case GDK_GRAB_NOT_VIEWABLE:
+ return "GrabNotViewable";
+ case GDK_GRAB_FROZEN:
+ return "GrabFrozen";
+ default:
+ {
+ static char foo [255];
+ sprintf (foo, "unknown status: %d", status);
+ return foo;
+ }
+ }
+}
+
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+/* This function enables and disables the Ctrl-Alt-KP_star and
+ Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
+ grabs and/or kill the grabbing client. That would effectively
+ unlock the screen, so we don't like that.
+
+ The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
+ if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
+ in XF86Config. I believe they are disabled by default.
+
+ This does not affect any other keys (specifically Ctrl-Alt-BS or
+ Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
+ */
+static void
+xorg_lock_smasher_set_active (GSGrab *grab,
+ gboolean active)
+{
+ int status, event, error;
+
+ if (!XF86MiscQueryExtension (GDK_DISPLAY (), &event, &error))
+ {
+ gs_debug ("No XFree86-Misc extension present");
+ return;
+ }
+
+ if (active)
+ {
+ gs_debug ("Enabling the x.org grab smasher");
+ }
+ else
+ {
+ gs_debug ("Disabling the x.org grab smasher");
+ }
+
+ gdk_error_trap_push ();
+
+ status = XF86MiscSetGrabKeysState (GDK_DISPLAY (), active);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+
+ if (active && status == MiscExtGrabStateAlready)
+ {
+ /* shut up, consider this success */
+ status = MiscExtGrabStateSuccess;
+ }
+
+ gs_debug ("XF86MiscSetGrabKeysState(%s) returned %s\n",
+ active ? "on" : "off",
+ (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
+ status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
+ status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
+ "unknown value"));
+}
+#else
+static void
+xorg_lock_smasher_set_active (GSGrab *grab,
+ gboolean active)
+{
+}
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
+static int
+gs_grab_get_keyboard (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen)
+{
+ GdkGrabStatus status;
+
+ g_return_val_if_fail (window != NULL, FALSE);
+ g_return_val_if_fail (screen != NULL, FALSE);
+
+ gs_debug ("Grabbing keyboard widget=%X", (guint32) GDK_WINDOW_XID (window));
+ status = gdk_keyboard_grab (window, FALSE, GDK_CURRENT_TIME);
+
+ if (status == GDK_GRAB_SUCCESS)
+ {
+ if (grab->priv->keyboard_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
+ (gpointer *) &grab->priv->keyboard_grab_window);
+ }
+ grab->priv->keyboard_grab_window = window;
+
+ g_object_add_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
+ (gpointer *) &grab->priv->keyboard_grab_window);
+
+ grab->priv->keyboard_grab_screen = screen;
+ }
+ else
+ {
+ gs_debug ("Couldn't grab keyboard! (%s)", grab_string (status));
+ }
+
+ return status;
+}
+
+static int
+gs_grab_get_mouse (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ GdkGrabStatus status;
+ GdkCursor *cursor;
+
+ g_return_val_if_fail (window != NULL, FALSE);
+ g_return_val_if_fail (screen != NULL, FALSE);
+
+ cursor = get_cursor ();
+
+ gs_debug ("Grabbing mouse widget=%X", (guint32) GDK_WINDOW_XID (window));
+ status = gdk_pointer_grab (window, TRUE, 0, NULL,
+ (hide_cursor ? cursor : NULL),
+ GDK_CURRENT_TIME);
+
+ if (status == GDK_GRAB_SUCCESS)
+ {
+ if (grab->priv->mouse_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window),
+ (gpointer *) &grab->priv->mouse_grab_window);
+ }
+ grab->priv->mouse_grab_window = window;
+
+ g_object_add_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window),
+ (gpointer *) &grab->priv->mouse_grab_window);
+
+ grab->priv->mouse_grab_screen = screen;
+ grab->priv->mouse_hide_cursor = hide_cursor;
+ }
+
+ gdk_cursor_unref (cursor);
+
+ return status;
+}
+
+void
+gs_grab_keyboard_reset (GSGrab *grab)
+{
+ if (grab->priv->keyboard_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->keyboard_grab_window),
+ (gpointer *) &grab->priv->keyboard_grab_window);
+ }
+ grab->priv->keyboard_grab_window = NULL;
+ grab->priv->keyboard_grab_screen = NULL;
+}
+
+static gboolean
+gs_grab_release_keyboard (GSGrab *grab)
+{
+ gs_debug ("Ungrabbing keyboard");
+ gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+
+ gs_grab_keyboard_reset (grab);
+
+ return TRUE;
+}
+
+void
+gs_grab_mouse_reset (GSGrab *grab)
+{
+ if (grab->priv->mouse_grab_window != NULL)
+ {
+ g_object_remove_weak_pointer (G_OBJECT (grab->priv->mouse_grab_window),
+ (gpointer *) &grab->priv->mouse_grab_window);
+ }
+
+ grab->priv->mouse_grab_window = NULL;
+ grab->priv->mouse_grab_screen = NULL;
+}
+
+gboolean
+gs_grab_release_mouse (GSGrab *grab)
+{
+ gs_debug ("Ungrabbing pointer");
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+
+ gs_grab_mouse_reset (grab);
+
+ return TRUE;
+}
+
+static gboolean
+gs_grab_move_mouse (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ gboolean result;
+ GdkWindow *old_window;
+ GdkScreen *old_screen;
+ gboolean old_hide_cursor;
+
+ /* if the pointer is not grabbed and we have a
+ mouse_grab_window defined then we lost the grab */
+ if (! gdk_pointer_is_grabbed ())
+ {
+ gs_grab_mouse_reset (grab);
+ }
+
+ if (grab->priv->mouse_grab_window == window)
+ {
+ gs_debug ("Window %X is already grabbed, skipping",
+ (guint32) GDK_WINDOW_XID (grab->priv->mouse_grab_window));
+ return TRUE;
+ }
+
+#if 0
+ gs_debug ("Intentionally skipping move pointer grabs");
+ /* FIXME: GTK doesn't like having the pointer grabbed */
+ return TRUE;
+#else
+ if (grab->priv->mouse_grab_window)
+ {
+ gs_debug ("Moving pointer grab from %X to %X",
+ (guint32) GDK_WINDOW_XID (grab->priv->mouse_grab_window),
+ (guint32) GDK_WINDOW_XID (window));
+ }
+ else
+ {
+ gs_debug ("Getting pointer grab on %X",
+ (guint32) GDK_WINDOW_XID (window));
+ }
+#endif
+
+ gs_debug ("*** doing X server grab");
+ gdk_x11_grab_server ();
+
+ old_window = grab->priv->mouse_grab_window;
+ old_screen = grab->priv->mouse_grab_screen;
+ old_hide_cursor = grab->priv->mouse_hide_cursor;
+
+ if (old_window)
+ {
+ gs_grab_release_mouse (grab);
+ }
+
+ result = gs_grab_get_mouse (grab, window, screen, hide_cursor);
+
+ if (result != GDK_GRAB_SUCCESS)
+ {
+ sleep (1);
+ result = gs_grab_get_mouse (grab, window, screen, hide_cursor);
+ }
+
+ if ((result != GDK_GRAB_SUCCESS) && old_window)
+ {
+ gs_debug ("Could not grab mouse for new window. Resuming previous grab.");
+ gs_grab_get_mouse (grab, old_window, old_screen, old_hide_cursor);
+ }
+
+ gs_debug ("*** releasing X server grab");
+ gdk_x11_ungrab_server ();
+ gdk_flush ();
+
+ return (result == GDK_GRAB_SUCCESS);
+}
+
+static gboolean
+gs_grab_move_keyboard (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen)
+{
+ gboolean result;
+ GdkWindow *old_window;
+ GdkScreen *old_screen;
+
+ if (grab->priv->keyboard_grab_window == window)
+ {
+ gs_debug ("Window %X is already grabbed, skipping",
+ (guint32) GDK_WINDOW_XID (grab->priv->keyboard_grab_window));
+ return TRUE;
+ }
+
+ if (grab->priv->keyboard_grab_window != NULL)
+ {
+ gs_debug ("Moving keyboard grab from %X to %X",
+ (guint32) GDK_WINDOW_XID (grab->priv->keyboard_grab_window),
+ (guint32) GDK_WINDOW_XID (window));
+ }
+ else
+ {
+ gs_debug ("Getting keyboard grab on %X",
+ (guint32) GDK_WINDOW_XID (window));
+
+ }
+
+ gs_debug ("*** doing X server grab");
+ gdk_x11_grab_server ();
+
+ old_window = grab->priv->keyboard_grab_window;
+ old_screen = grab->priv->keyboard_grab_screen;
+
+ if (old_window)
+ {
+ gs_grab_release_keyboard (grab);
+ }
+
+ result = gs_grab_get_keyboard (grab, window, screen);
+
+ if (result != GDK_GRAB_SUCCESS)
+ {
+ sleep (1);
+ result = gs_grab_get_keyboard (grab, window, screen);
+ }
+
+ if ((result != GDK_GRAB_SUCCESS) && old_window)
+ {
+ gs_debug ("Could not grab keyboard for new window. Resuming previous grab.");
+ gs_grab_get_keyboard (grab, old_window, old_screen);
+ }
+
+ gs_debug ("*** releasing X server grab");
+ gdk_x11_ungrab_server ();
+ gdk_flush ();
+
+ return (result == GDK_GRAB_SUCCESS);
+}
+
+static void
+gs_grab_nuke_focus (void)
+{
+ Window focus = 0;
+ int rev = 0;
+
+ gs_debug ("Nuking focus");
+
+ gdk_error_trap_push ();
+
+ XGetInputFocus (GDK_DISPLAY (), &focus, &rev);
+
+ XSetInputFocus (GDK_DISPLAY (), None, RevertToNone, CurrentTime);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+}
+
+void
+gs_grab_release (GSGrab *grab)
+{
+ gs_debug ("Releasing all grabs");
+
+ gs_grab_release_mouse (grab);
+ gs_grab_release_keyboard (grab);
+
+ /* FIXME: is it right to enable this ? */
+ xorg_lock_smasher_set_active (grab, TRUE);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_flush ();
+}
+
+gboolean
+gs_grab_grab_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ gboolean mstatus = FALSE;
+ gboolean kstatus = FALSE;
+ int i;
+ int retries = 4;
+ gboolean focus_fuckus = FALSE;
+
+AGAIN:
+
+ for (i = 0; i < retries; i++)
+ {
+ kstatus = gs_grab_get_keyboard (grab, window, screen);
+ if (kstatus == GDK_GRAB_SUCCESS)
+ {
+ break;
+ }
+
+ /* else, wait a second and try to grab again. */
+ sleep (1);
+ }
+
+ if (kstatus != GDK_GRAB_SUCCESS)
+ {
+ if (!focus_fuckus)
+ {
+ focus_fuckus = TRUE;
+ gs_grab_nuke_focus ();
+ goto AGAIN;
+ }
+ }
+
+ for (i = 0; i < retries; i++)
+ {
+ mstatus = gs_grab_get_mouse (grab, window, screen, hide_cursor);
+ if (mstatus == GDK_GRAB_SUCCESS)
+ {
+ break;
+ }
+
+ /* else, wait a second and try to grab again. */
+ sleep (1);
+ }
+
+ if (mstatus != GDK_GRAB_SUCCESS)
+ {
+ gs_debug ("Couldn't grab pointer! (%s)",
+ grab_string (mstatus));
+ }
+
+#if 0
+ /* FIXME: release the pointer grab so GTK will work */
+ gs_grab_release_mouse (grab);
+#endif
+
+ /* When should we allow blanking to proceed? The current theory
+ is that both a keyboard grab and a mouse grab are mandatory
+
+ - If we don't have a keyboard grab, then we won't be able to
+ read a password to unlock, so the kbd grab is manditory.
+
+ - If we don't have a mouse grab, then we might not see mouse
+ clicks as a signal to unblank, on-screen widgets won't work ideally,
+ and gs_grab_move_to_window() will spin forever when it gets called.
+ */
+
+ if (kstatus != GDK_GRAB_SUCCESS || mstatus != GDK_GRAB_SUCCESS)
+ {
+ /* Do not blank without a keyboard and mouse grabs. */
+
+ /* Release keyboard or mouse which was grabbed. */
+ if (kstatus == GDK_GRAB_SUCCESS)
+ {
+ gs_grab_release_keyboard (grab);
+ }
+ if (mstatus == GDK_GRAB_SUCCESS)
+ {
+ gs_grab_release_mouse (grab);
+ }
+
+ return FALSE;
+ }
+
+ /* Grab is good, go ahead and blank. */
+ return TRUE;
+}
+
+/* this is used to grab the keyboard and mouse to the root */
+gboolean
+gs_grab_grab_root (GSGrab *grab,
+ gboolean hide_cursor)
+{
+ GdkDisplay *display;
+ GdkWindow *root;
+ GdkScreen *screen;
+ gboolean res;
+
+ gs_debug ("Grabbing the root window");
+
+ display = gdk_display_get_default ();
+ gdk_display_get_pointer (display, &screen, NULL, NULL, NULL);
+ root = gdk_screen_get_root_window (screen);
+
+ res = gs_grab_grab_window (grab, root, screen, hide_cursor);
+
+ return res;
+}
+
+/* this is used to grab the keyboard and mouse to an offscreen window */
+gboolean
+gs_grab_grab_offscreen (GSGrab *grab,
+ gboolean hide_cursor)
+{
+ GdkScreen *screen;
+ gboolean res;
+
+ gs_debug ("Grabbing an offscreen window");
+
+ screen = gtk_invisible_get_screen (GTK_INVISIBLE (grab->priv->invisible));
+ res = gs_grab_grab_window (grab, grab->priv->invisible->window, screen, hide_cursor);
+
+ return res;
+}
+
+/* This is similar to gs_grab_grab_window but doesn't fail */
+void
+gs_grab_move_to_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor)
+{
+ gboolean result = FALSE;
+
+ g_return_if_fail (GS_IS_GRAB (grab));
+
+ xorg_lock_smasher_set_active (grab, FALSE);
+
+ do
+ {
+ result = gs_grab_move_keyboard (grab, window, screen);
+ gdk_flush ();
+ }
+ while (!result);
+
+ do
+ {
+ result = gs_grab_move_mouse (grab, window, screen, hide_cursor);
+ gdk_flush ();
+ }
+ while (!result);
+}
+
+static void
+gs_grab_class_init (GSGrabClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_grab_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSGrabPrivate));
+}
+
+static void
+gs_grab_init (GSGrab *grab)
+{
+ grab->priv = GS_GRAB_GET_PRIVATE (grab);
+
+ grab->priv->mouse_hide_cursor = FALSE;
+ grab->priv->invisible = gtk_invisible_new ();
+ gtk_widget_show (grab->priv->invisible);
+}
+
+static void
+gs_grab_finalize (GObject *object)
+{
+ GSGrab *grab;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_GRAB (object));
+
+ grab = GS_GRAB (object);
+
+ g_return_if_fail (grab->priv != NULL);
+
+ gtk_widget_destroy (grab->priv->invisible);
+
+ G_OBJECT_CLASS (gs_grab_parent_class)->finalize (object);
+}
+
+GSGrab *
+gs_grab_new (void)
+{
+ if (grab_object)
+ {
+ g_object_ref (grab_object);
+ }
+ else
+ {
+ grab_object = g_object_new (GS_TYPE_GRAB, NULL);
+ g_object_add_weak_pointer (grab_object,
+ (gpointer *) &grab_object);
+ }
+
+ return GS_GRAB (grab_object);
+}
diff --git a/src/gs-grab.h b/src/gs-grab.h
new file mode 100644
index 0000000..4717f49
--- /dev/null
+++ b/src/gs-grab.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_GRAB_H
+#define __GS_GRAB_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_GRAB (gs_grab_get_type ())
+#define GS_GRAB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_GRAB, GSGrab))
+#define GS_GRAB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_GRAB, GSGrabClass))
+#define GS_IS_GRAB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_GRAB))
+#define GS_IS_GRAB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_GRAB))
+#define GS_GRAB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_GRAB, GSGrabClass))
+
+typedef struct GSGrabPrivate GSGrabPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSGrabPrivate *priv;
+} GSGrab;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+} GSGrabClass;
+
+GType gs_grab_get_type (void);
+
+GSGrab * gs_grab_new (void);
+
+void gs_grab_release (GSGrab *grab);
+gboolean gs_grab_release_mouse (GSGrab *grab);
+
+gboolean gs_grab_grab_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor);
+
+gboolean gs_grab_grab_root (GSGrab *grab,
+ gboolean hide_cursor);
+gboolean gs_grab_grab_offscreen (GSGrab *grab,
+ gboolean hide_cursor);
+
+void gs_grab_move_to_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor);
+
+void gs_grab_mouse_reset (GSGrab *grab);
+void gs_grab_keyboard_reset (GSGrab *grab);
+
+G_END_DECLS
+
+#endif /* __GS_GRAB_H */
diff --git a/src/gs-grab.h.orig b/src/gs-grab.h.orig
new file mode 100644
index 0000000..a910403
--- /dev/null
+++ b/src/gs-grab.h.orig
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_GRAB_H
+#define __GS_GRAB_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_GRAB (gs_grab_get_type ())
+#define GS_GRAB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_GRAB, GSGrab))
+#define GS_GRAB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_GRAB, GSGrabClass))
+#define GS_IS_GRAB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_GRAB))
+#define GS_IS_GRAB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_GRAB))
+#define GS_GRAB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_GRAB, GSGrabClass))
+
+typedef struct GSGrabPrivate GSGrabPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSGrabPrivate *priv;
+} GSGrab;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+} GSGrabClass;
+
+GType gs_grab_get_type (void);
+
+GSGrab * gs_grab_new (void);
+
+void gs_grab_release (GSGrab *grab);
+gboolean gs_grab_release_mouse (GSGrab *grab);
+
+gboolean gs_grab_grab_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor);
+
+gboolean gs_grab_grab_root (GSGrab *grab,
+ gboolean hide_cursor);
+gboolean gs_grab_grab_offscreen (GSGrab *grab,
+ gboolean hide_cursor);
+
+void gs_grab_move_to_window (GSGrab *grab,
+ GdkWindow *window,
+ GdkScreen *screen,
+ gboolean hide_cursor);
+
+void gs_grab_mouse_reset (GSGrab *grab);
+void gs_grab_keyboard_reset (GSGrab *grab);
+
+G_END_DECLS
+
+#endif /* __GS_GRAB_H */
diff --git a/src/gs-job.c b/src/gs-job.c
new file mode 100644
index 0000000..30d1f34
--- /dev/null
+++ b/src/gs-job.c
@@ -0,0 +1,567 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <string.h>
+
+#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+#include <sys/resource.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include "gs-debug.h"
+#include "gs-job.h"
+
+#include "subprocs.h"
+
+static void gs_job_class_init (GSJobClass *klass);
+static void gs_job_init (GSJob *job);
+static void gs_job_finalize (GObject *object);
+
+#define GS_JOB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_JOB, GSJobPrivate))
+
+typedef enum
+{
+ GS_JOB_INVALID,
+ GS_JOB_RUNNING,
+ GS_JOB_STOPPED,
+ GS_JOB_KILLED,
+ GS_JOB_DEAD
+} GSJobStatus;
+
+struct GSJobPrivate
+{
+ GtkWidget *widget;
+
+ GSJobStatus status;
+ gint pid;
+ guint watch_id;
+
+ char *command;
+};
+
+G_DEFINE_TYPE (GSJob, gs_job, G_TYPE_OBJECT)
+
+static char *
+widget_get_id_string (GtkWidget *widget)
+{
+ char *id = NULL;
+
+ g_return_val_if_fail (widget != NULL, NULL);
+
+ id = g_strdup_printf ("0x%X",
+ (guint32)GDK_WINDOW_XID (widget->window));
+ return id;
+}
+
+static void
+gs_job_class_init (GSJobClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_job_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSJobPrivate));
+}
+
+static void
+gs_job_init (GSJob *job)
+{
+ job->priv = GS_JOB_GET_PRIVATE (job);
+}
+
+/* adapted from gspawn.c */
+static int
+wait_on_child (int pid)
+{
+ int status;
+
+wait_again:
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ {
+ goto wait_again;
+ }
+ else if (errno == ECHILD)
+ {
+ ; /* do nothing, child already reaped */
+ }
+ else
+ {
+ gs_debug ("waitpid () should not fail in 'GSJob'");
+ }
+ }
+
+ return status;
+}
+
+static void
+gs_job_died (GSJob *job)
+{
+ if (job->priv->pid > 0)
+ {
+ int exit_status;
+
+ gs_debug ("Waiting on process %d", job->priv->pid);
+ exit_status = wait_on_child (job->priv->pid);
+
+ job->priv->status = GS_JOB_DEAD;
+
+ if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0))
+ {
+ gs_debug ("Wait on child process failed");
+ }
+ else
+ {
+ /* exited normally */
+ }
+ }
+ g_spawn_close_pid (job->priv->pid);
+ job->priv->pid = 0;
+
+ gs_debug ("Job died");
+}
+
+static void
+gs_job_finalize (GObject *object)
+{
+ GSJob *job;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_JOB (object));
+
+ job = GS_JOB (object);
+
+ g_return_if_fail (job->priv != NULL);
+
+ if (job->priv->pid > 0)
+ {
+ signal_pid (job->priv->pid, SIGTERM);
+ gs_job_died (job);
+ }
+
+ g_free (job->priv->command);
+ job->priv->command = NULL;
+
+ G_OBJECT_CLASS (gs_job_parent_class)->finalize (object);
+}
+
+void
+gs_job_set_widget (GSJob *job,
+ GtkWidget *widget)
+{
+ g_return_if_fail (job != NULL);
+ g_return_if_fail (GS_IS_JOB (job));
+
+ if (widget != job->priv->widget)
+ {
+ job->priv->widget = widget;
+
+ /* restart job */
+ if (gs_job_is_running (job))
+ {
+ gs_job_stop (job);
+ gs_job_start (job);
+ }
+ }
+}
+
+gboolean
+gs_job_set_command (GSJob *job,
+ const char *command)
+{
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("Setting command for job: '%s'",
+ command != NULL ? command : "NULL");
+
+ g_free (job->priv->command);
+ job->priv->command = g_strdup (command);
+
+ return TRUE;
+}
+
+GSJob *
+gs_job_new (void)
+{
+ GObject *job;
+
+ job = g_object_new (GS_TYPE_JOB, NULL);
+
+ return GS_JOB (job);
+}
+
+GSJob *
+gs_job_new_for_widget (GtkWidget *widget)
+{
+ GObject *job;
+
+ job = g_object_new (GS_TYPE_JOB, NULL);
+
+ gs_job_set_widget (GS_JOB (job), widget);
+
+ return GS_JOB (job);
+}
+
+static void
+nice_process (int pid,
+ int nice_level)
+{
+ g_return_if_fail (pid > 0);
+
+ if (nice_level == 0)
+ {
+ return;
+ }
+
+#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+ gs_debug ("Setting child process priority to: %d", nice_level);
+ if (setpriority (PRIO_PROCESS, pid, nice_level) != 0)
+ {
+ gs_debug ("setpriority(PRIO_PROCESS, %lu, %d) failed",
+ (unsigned long) pid, nice_level);
+ }
+#else
+ gs_debug ("don't know how to change process priority on this system.");
+#endif
+}
+
+static GPtrArray *
+get_env_vars (GtkWidget *widget)
+{
+ GPtrArray *env;
+ char *str;
+ int i;
+ static const char *allowed_env_vars [] =
+ {
+ "PATH",
+ "SESSION_MANAGER",
+ "XAUTHORITY",
+ "XAUTHLOCALHOSTNAME",
+ "LANG",
+ "LANGUAGE",
+ "DBUS_SESSION_BUS_ADDRESS"
+ };
+
+ env = g_ptr_array_new ();
+
+ str = gdk_screen_make_display_name (gtk_widget_get_screen (widget));
+ g_ptr_array_add (env, g_strdup_printf ("DISPLAY=%s", str));
+ g_free (str);
+
+ g_ptr_array_add (env, g_strdup_printf ("HOME=%s",
+ g_get_home_dir ()));
+
+ for (i = 0; i < G_N_ELEMENTS (allowed_env_vars); i++)
+ {
+ const char *var;
+ const char *val;
+ var = allowed_env_vars [i];
+ val = g_getenv (var);
+ if (val != NULL)
+ {
+ g_ptr_array_add (env, g_strdup_printf ("%s=%s",
+ var,
+ val));
+ }
+ }
+
+ str = widget_get_id_string (widget);
+ g_ptr_array_add (env, g_strdup_printf ("XSCREENSAVER_WINDOW=%s", str));
+ g_free (str);
+
+ g_ptr_array_add (env, NULL);
+
+ return env;
+}
+
+static gboolean
+spawn_on_widget (GtkWidget *widget,
+ const char *command,
+ int *pid,
+ GIOFunc watch_func,
+ gpointer user_data,
+ guint *watch_id)
+{
+ char **argv;
+ GPtrArray *env;
+ gboolean result;
+ GIOChannel *channel;
+ GError *error = NULL;
+ int standard_error;
+ int child_pid;
+ int id;
+ int i;
+
+ if (command == NULL)
+ {
+ return FALSE;
+ }
+
+ if (! g_shell_parse_argv (command, NULL, &argv, &error))
+ {
+ gs_debug ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ env = get_env_vars (widget);
+
+ error = NULL;
+ result = gdk_spawn_on_screen_with_pipes (gtk_widget_get_screen (widget),
+ NULL,
+ argv,
+ (char **)env->pdata,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL,
+ NULL,
+ &child_pid,
+ NULL,
+ NULL,
+ &standard_error,
+ &error);
+ for (i = 0; i < env->len; i++)
+ {
+ g_free (g_ptr_array_index (env, i));
+ }
+ g_ptr_array_free (env, TRUE);
+
+ if (! result)
+ {
+ gs_debug ("Could not start command '%s': %s", command, error->message);
+ g_error_free (error);
+ g_strfreev (argv);
+ return FALSE;
+ }
+
+ g_strfreev (argv);
+
+ nice_process (child_pid, 10);
+
+ if (pid != NULL)
+ {
+ *pid = child_pid;
+ }
+ else
+ {
+ g_spawn_close_pid (child_pid);
+ }
+
+ channel = g_io_channel_unix_new (standard_error);
+ 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);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ watch_func,
+ user_data);
+ if (watch_id != NULL)
+ {
+ *watch_id = id;
+ }
+
+ g_io_channel_unref (channel);
+
+ return result;
+}
+
+static gboolean
+command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSJob *job)
+{
+ GIOStatus status;
+ GError *error = NULL;
+ gboolean done = FALSE;
+
+ g_return_val_if_fail (job != NULL, FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ char *str;
+
+ status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
+
+ if (status == G_IO_STATUS_NORMAL)
+ {
+ gs_debug ("command output: %s", str);
+
+ }
+ else if (status == G_IO_STATUS_EOF)
+ {
+ done = TRUE;
+
+ }
+ else if (error != NULL)
+ {
+ gs_debug ("command error: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_free (str);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ done = TRUE;
+ }
+
+ if (done)
+ {
+ gs_job_died (job);
+
+ job->priv->watch_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_job_is_running (GSJob *job)
+{
+ gboolean running;
+
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ running = (job->priv->pid > 0);
+
+ return running;
+}
+
+gboolean
+gs_job_start (GSJob *job)
+{
+ gboolean result;
+
+ g_return_val_if_fail (job != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("starting job");
+
+ if (job->priv->pid != 0)
+ {
+ gs_debug ("Cannot restart active job.");
+ return FALSE;
+ }
+
+ if (job->priv->widget == NULL)
+ {
+ gs_debug ("Could not start job: screensaver window is not set.");
+ return FALSE;
+ }
+
+ if (job->priv->command == NULL)
+ {
+ /* no warning here because a NULL command is interpreted
+ as a no-op job */
+ gs_debug ("No command set for job.");
+ return FALSE;
+ }
+
+ result = spawn_on_widget (job->priv->widget,
+ job->priv->command,
+ &job->priv->pid,
+ (GIOFunc)command_watch,
+ job,
+ &job->priv->watch_id);
+
+ if (result)
+ {
+ job->priv->status = GS_JOB_RUNNING;
+ }
+
+ return result;
+}
+
+static void
+remove_command_watch (GSJob *job)
+{
+ if (job->priv->watch_id != 0)
+ {
+ g_source_remove (job->priv->watch_id);
+ job->priv->watch_id = 0;
+ }
+}
+
+gboolean
+gs_job_stop (GSJob *job)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("stopping job");
+
+ if (job->priv->pid == 0)
+ {
+ gs_debug ("Could not stop job: pid not defined");
+ return FALSE;
+ }
+
+ if (job->priv->status == GS_JOB_STOPPED)
+ {
+ gs_job_suspend (job, FALSE);
+ }
+
+ remove_command_watch (job);
+
+ signal_pid (job->priv->pid, SIGTERM);
+
+ job->priv->status = GS_JOB_KILLED;
+
+ gs_job_died (job);
+
+ return TRUE;
+}
+
+gboolean
+gs_job_suspend (GSJob *job,
+ gboolean suspend)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("suspending job");
+
+ if (job->priv->pid == 0)
+ {
+ return FALSE;
+ }
+
+ signal_pid (job->priv->pid, (suspend ? SIGSTOP : SIGCONT));
+
+ job->priv->status = (suspend ? GS_JOB_STOPPED : GS_JOB_RUNNING);
+
+ return TRUE;
+}
diff --git a/src/gs-job.c.orig b/src/gs-job.c.orig
new file mode 100644
index 0000000..896addc
--- /dev/null
+++ b/src/gs-job.c.orig
@@ -0,0 +1,567 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <string.h>
+
+#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+#include <sys/resource.h>
+#endif
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include "gs-debug.h"
+#include "gs-job.h"
+
+#include "subprocs.h"
+
+static void gs_job_class_init (GSJobClass *klass);
+static void gs_job_init (GSJob *job);
+static void gs_job_finalize (GObject *object);
+
+#define GS_JOB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_JOB, GSJobPrivate))
+
+typedef enum
+{
+ GS_JOB_INVALID,
+ GS_JOB_RUNNING,
+ GS_JOB_STOPPED,
+ GS_JOB_KILLED,
+ GS_JOB_DEAD
+} GSJobStatus;
+
+struct GSJobPrivate
+{
+ GtkWidget *widget;
+
+ GSJobStatus status;
+ gint pid;
+ guint watch_id;
+
+ char *command;
+};
+
+G_DEFINE_TYPE (GSJob, gs_job, G_TYPE_OBJECT)
+
+static char *
+widget_get_id_string (GtkWidget *widget)
+{
+ char *id = NULL;
+
+ g_return_val_if_fail (widget != NULL, NULL);
+
+ id = g_strdup_printf ("0x%X",
+ (guint32)GDK_WINDOW_XID (widget->window));
+ return id;
+}
+
+static void
+gs_job_class_init (GSJobClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_job_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSJobPrivate));
+}
+
+static void
+gs_job_init (GSJob *job)
+{
+ job->priv = GS_JOB_GET_PRIVATE (job);
+}
+
+/* adapted from gspawn.c */
+static int
+wait_on_child (int pid)
+{
+ int status;
+
+wait_again:
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ {
+ goto wait_again;
+ }
+ else if (errno == ECHILD)
+ {
+ ; /* do nothing, child already reaped */
+ }
+ else
+ {
+ gs_debug ("waitpid () should not fail in 'GSJob'");
+ }
+ }
+
+ return status;
+}
+
+static void
+gs_job_died (GSJob *job)
+{
+ if (job->priv->pid > 0)
+ {
+ int exit_status;
+
+ gs_debug ("Waiting on process %d", job->priv->pid);
+ exit_status = wait_on_child (job->priv->pid);
+
+ job->priv->status = GS_JOB_DEAD;
+
+ if (WIFEXITED (exit_status) && (WEXITSTATUS (exit_status) != 0))
+ {
+ gs_debug ("Wait on child process failed");
+ }
+ else
+ {
+ /* exited normally */
+ }
+ }
+ g_spawn_close_pid (job->priv->pid);
+ job->priv->pid = 0;
+
+ gs_debug ("Job died");
+}
+
+static void
+gs_job_finalize (GObject *object)
+{
+ GSJob *job;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_JOB (object));
+
+ job = GS_JOB (object);
+
+ g_return_if_fail (job->priv != NULL);
+
+ if (job->priv->pid > 0)
+ {
+ signal_pid (job->priv->pid, SIGTERM);
+ gs_job_died (job);
+ }
+
+ g_free (job->priv->command);
+ job->priv->command = NULL;
+
+ G_OBJECT_CLASS (gs_job_parent_class)->finalize (object);
+}
+
+void
+gs_job_set_widget (GSJob *job,
+ GtkWidget *widget)
+{
+ g_return_if_fail (job != NULL);
+ g_return_if_fail (GS_IS_JOB (job));
+
+ if (widget != job->priv->widget)
+ {
+ job->priv->widget = widget;
+
+ /* restart job */
+ if (gs_job_is_running (job))
+ {
+ gs_job_stop (job);
+ gs_job_start (job);
+ }
+ }
+}
+
+gboolean
+gs_job_set_command (GSJob *job,
+ const char *command)
+{
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("Setting command for job: '%s'",
+ command != NULL ? command : "NULL");
+
+ g_free (job->priv->command);
+ job->priv->command = g_strdup (command);
+
+ return TRUE;
+}
+
+GSJob *
+gs_job_new (void)
+{
+ GObject *job;
+
+ job = g_object_new (GS_TYPE_JOB, NULL);
+
+ return GS_JOB (job);
+}
+
+GSJob *
+gs_job_new_for_widget (GtkWidget *widget)
+{
+ GObject *job;
+
+ job = g_object_new (GS_TYPE_JOB, NULL);
+
+ gs_job_set_widget (GS_JOB (job), widget);
+
+ return GS_JOB (job);
+}
+
+static void
+nice_process (int pid,
+ int nice_level)
+{
+ g_return_if_fail (pid > 0);
+
+ if (nice_level == 0)
+ {
+ return;
+ }
+
+#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+ gs_debug ("Setting child process priority to: %d", nice_level);
+ if (setpriority (PRIO_PROCESS, pid, nice_level) != 0)
+ {
+ gs_debug ("setpriority(PRIO_PROCESS, %lu, %d) failed",
+ (unsigned long) pid, nice_level);
+ }
+#else
+ gs_debug ("don't know how to change process priority on this system.");
+#endif
+}
+
+static GPtrArray *
+get_env_vars (GtkWidget *widget)
+{
+ GPtrArray *env;
+ char *str;
+ int i;
+ static const char *allowed_env_vars [] =
+ {
+ "PATH",
+ "SESSION_MANAGER",
+ "XAUTHORITY",
+ "XAUTHLOCALHOSTNAME",
+ "LANG",
+ "LANGUAGE",
+ "DBUS_SESSION_BUS_ADDRESS"
+ };
+
+ env = g_ptr_array_new ();
+
+ str = gdk_screen_make_display_name (gtk_widget_get_screen (widget));
+ g_ptr_array_add (env, g_strdup_printf ("DISPLAY=%s", str));
+ g_free (str);
+
+ g_ptr_array_add (env, g_strdup_printf ("HOME=%s",
+ g_get_home_dir ()));
+
+ for (i = 0; i < G_N_ELEMENTS (allowed_env_vars); i++)
+ {
+ const char *var;
+ const char *val;
+ var = allowed_env_vars [i];
+ val = g_getenv (var);
+ if (val != NULL)
+ {
+ g_ptr_array_add (env, g_strdup_printf ("%s=%s",
+ var,
+ val));
+ }
+ }
+
+ str = widget_get_id_string (widget);
+ g_ptr_array_add (env, g_strdup_printf ("XSCREENSAVER_WINDOW=%s", str));
+ g_free (str);
+
+ g_ptr_array_add (env, NULL);
+
+ return env;
+}
+
+static gboolean
+spawn_on_widget (GtkWidget *widget,
+ const char *command,
+ int *pid,
+ GIOFunc watch_func,
+ gpointer user_data,
+ guint *watch_id)
+{
+ char **argv;
+ GPtrArray *env;
+ gboolean result;
+ GIOChannel *channel;
+ GError *error = NULL;
+ int standard_error;
+ int child_pid;
+ int id;
+ int i;
+
+ if (command == NULL)
+ {
+ return FALSE;
+ }
+
+ if (! g_shell_parse_argv (command, NULL, &argv, &error))
+ {
+ gs_debug ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ env = get_env_vars (widget);
+
+ error = NULL;
+ result = gdk_spawn_on_screen_with_pipes (gtk_widget_get_screen (widget),
+ NULL,
+ argv,
+ (char **)env->pdata,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+ NULL,
+ NULL,
+ &child_pid,
+ NULL,
+ NULL,
+ &standard_error,
+ &error);
+ for (i = 0; i < env->len; i++)
+ {
+ g_free (g_ptr_array_index (env, i));
+ }
+ g_ptr_array_free (env, TRUE);
+
+ if (! result)
+ {
+ gs_debug ("Could not start command '%s': %s", command, error->message);
+ g_error_free (error);
+ g_strfreev (argv);
+ return FALSE;
+ }
+
+ g_strfreev (argv);
+
+ nice_process (child_pid, 10);
+
+ if (pid != NULL)
+ {
+ *pid = child_pid;
+ }
+ else
+ {
+ g_spawn_close_pid (child_pid);
+ }
+
+ channel = g_io_channel_unix_new (standard_error);
+ 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);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ watch_func,
+ user_data);
+ if (watch_id != NULL)
+ {
+ *watch_id = id;
+ }
+
+ g_io_channel_unref (channel);
+
+ return result;
+}
+
+static gboolean
+command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSJob *job)
+{
+ GIOStatus status;
+ GError *error = NULL;
+ gboolean done = FALSE;
+
+ g_return_val_if_fail (job != NULL, FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ char *str;
+
+ status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
+
+ if (status == G_IO_STATUS_NORMAL)
+ {
+ gs_debug ("command output: %s", str);
+
+ }
+ else if (status == G_IO_STATUS_EOF)
+ {
+ done = TRUE;
+
+ }
+ else if (error != NULL)
+ {
+ gs_debug ("command error: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_free (str);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ done = TRUE;
+ }
+
+ if (done)
+ {
+ gs_job_died (job);
+
+ job->priv->watch_id = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_job_is_running (GSJob *job)
+{
+ gboolean running;
+
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ running = (job->priv->pid > 0);
+
+ return running;
+}
+
+gboolean
+gs_job_start (GSJob *job)
+{
+ gboolean result;
+
+ g_return_val_if_fail (job != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("starting job");
+
+ if (job->priv->pid != 0)
+ {
+ gs_debug ("Cannot restart active job.");
+ return FALSE;
+ }
+
+ if (job->priv->widget == NULL)
+ {
+ gs_debug ("Could not start job: screensaver window is not set.");
+ return FALSE;
+ }
+
+ if (job->priv->command == NULL)
+ {
+ /* no warning here because a NULL command is interpreted
+ as a no-op job */
+ gs_debug ("No command set for job.");
+ return FALSE;
+ }
+
+ result = spawn_on_widget (job->priv->widget,
+ job->priv->command,
+ &job->priv->pid,
+ (GIOFunc)command_watch,
+ job,
+ &job->priv->watch_id);
+
+ if (result)
+ {
+ job->priv->status = GS_JOB_RUNNING;
+ }
+
+ return result;
+}
+
+static void
+remove_command_watch (GSJob *job)
+{
+ if (job->priv->watch_id != 0)
+ {
+ g_source_remove (job->priv->watch_id);
+ job->priv->watch_id = 0;
+ }
+}
+
+gboolean
+gs_job_stop (GSJob *job)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("stopping job");
+
+ if (job->priv->pid == 0)
+ {
+ gs_debug ("Could not stop job: pid not defined");
+ return FALSE;
+ }
+
+ if (job->priv->status == GS_JOB_STOPPED)
+ {
+ gs_job_suspend (job, FALSE);
+ }
+
+ remove_command_watch (job);
+
+ signal_pid (job->priv->pid, SIGTERM);
+
+ job->priv->status = GS_JOB_KILLED;
+
+ gs_job_died (job);
+
+ return TRUE;
+}
+
+gboolean
+gs_job_suspend (GSJob *job,
+ gboolean suspend)
+{
+ g_return_val_if_fail (job != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_JOB (job), FALSE);
+
+ gs_debug ("suspending job");
+
+ if (job->priv->pid == 0)
+ {
+ return FALSE;
+ }
+
+ signal_pid (job->priv->pid, (suspend ? SIGSTOP : SIGCONT));
+
+ job->priv->status = (suspend ? GS_JOB_STOPPED : GS_JOB_RUNNING);
+
+ return TRUE;
+}
diff --git a/src/gs-job.h b/src/gs-job.h
new file mode 100644
index 0000000..ba7084e
--- /dev/null
+++ b/src/gs-job.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_JOB_H
+#define __GS_JOB_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_JOB (gs_job_get_type ())
+#define GS_JOB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_JOB, GSJob))
+#define GS_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_JOB, GSJobClass))
+#define GS_IS_JOB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_JOB))
+#define GS_IS_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_JOB))
+#define GS_JOB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_JOB, GSJobClass))
+
+typedef struct GSJobPrivate GSJobPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSJobPrivate *priv;
+} GSJob;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GSJobClass;
+
+GType gs_job_get_type (void);
+
+GSJob *gs_job_new (void);
+GSJob *gs_job_new_for_widget (GtkWidget *widget);
+
+gboolean gs_job_is_running (GSJob *job);
+gboolean gs_job_start (GSJob *job);
+gboolean gs_job_stop (GSJob *job);
+gboolean gs_job_suspend (GSJob *job,
+ gboolean suspend);
+
+void gs_job_set_widget (GSJob *job,
+ GtkWidget *widget);
+
+gboolean gs_job_set_command (GSJob *job,
+ const char *command);
+
+G_END_DECLS
+
+#endif /* __GS_JOB_H */
diff --git a/src/gs-job.h.orig b/src/gs-job.h.orig
new file mode 100644
index 0000000..b4fa3dc
--- /dev/null
+++ b/src/gs-job.h.orig
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_JOB_H
+#define __GS_JOB_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_JOB (gs_job_get_type ())
+#define GS_JOB(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_JOB, GSJob))
+#define GS_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_JOB, GSJobClass))
+#define GS_IS_JOB(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_JOB))
+#define GS_IS_JOB_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_JOB))
+#define GS_JOB_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_JOB, GSJobClass))
+
+typedef struct GSJobPrivate GSJobPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSJobPrivate *priv;
+} GSJob;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GSJobClass;
+
+GType gs_job_get_type (void);
+
+GSJob *gs_job_new (void);
+GSJob *gs_job_new_for_widget (GtkWidget *widget);
+
+gboolean gs_job_is_running (GSJob *job);
+gboolean gs_job_start (GSJob *job);
+gboolean gs_job_stop (GSJob *job);
+gboolean gs_job_suspend (GSJob *job,
+ gboolean suspend);
+
+void gs_job_set_widget (GSJob *job,
+ GtkWidget *widget);
+
+gboolean gs_job_set_command (GSJob *job,
+ const char *command);
+
+G_END_DECLS
+
+#endif /* __GS_JOB_H */
diff --git a/src/gs-listener-dbus.c b/src/gs-listener-dbus.c
new file mode 100644
index 0000000..601689c
--- /dev/null
+++ b/src/gs-listener-dbus.c
@@ -0,0 +1,2272 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gs-listener-dbus.h"
+#include "gs-marshal.h"
+#include "gs-debug.h"
+
+/* this is for dbus < 0.3 */
+#if ((DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 30))
+#define dbus_bus_name_has_owner(connection, name, err) dbus_bus_service_exists(connection, name, err)
+#define dbus_bus_request_name(connection, name, flags, err) dbus_bus_acquire_service(connection, name, flags, err)
+#endif
+
+static void gs_listener_class_init (GSListenerClass *klass);
+static void gs_listener_init (GSListener *listener);
+static void gs_listener_finalize (GObject *object);
+
+static void gs_listener_unregister_handler (DBusConnection *connection,
+ void *data);
+
+static DBusHandlerResult gs_listener_message_handler (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+
+#define GS_LISTENER_SERVICE "org.mate.ScreenSaver"
+#define GS_LISTENER_PATH "/org/mate/ScreenSaver"
+#define GS_LISTENER_INTERFACE "org.mate.ScreenSaver"
+
+#define HAL_DEVICE_INTERFACE "org.freedesktop.Hal.Device"
+
+#define CK_NAME "org.freedesktop.ConsoleKit"
+#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
+
+#define SESSION_NAME "org.mate.SessionManager"
+#define SESSION_PATH "/org/mate/SessionManager"
+#define SESSION_INTERFACE "org.mate.SessionManager"
+
+#define TYPE_MISMATCH_ERROR GS_LISTENER_INTERFACE ".TypeMismatch"
+
+#define GS_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_LISTENER, GSListenerPrivate))
+
+struct GSListenerPrivate
+{
+ DBusConnection *connection;
+ DBusConnection *system_connection;
+
+ guint session_idle : 1;
+ guint active : 1;
+ guint activation_enabled : 1;
+ guint throttled : 1;
+ GHashTable *inhibitors;
+ GHashTable *throttlers;
+ time_t active_start;
+ time_t session_idle_start;
+ char *session_id;
+
+ guint32 ck_throttle_cookie;
+};
+
+typedef struct
+{
+ int entry_type;
+ char *application;
+ char *reason;
+ char *connection;
+ guint32 cookie;
+ guint32 foreign_cookie;
+ GTimeVal since;
+} GSListenerRefEntry;
+
+enum
+{
+ LOCK,
+ CYCLE,
+ QUIT,
+ SIMULATE_USER_ACTIVITY,
+ ACTIVE_CHANGED,
+ THROTTLE_CHANGED,
+ SHOW_MESSAGE,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_ACTIVE,
+ PROP_SESSION_IDLE,
+ PROP_ACTIVATION_ENABLED,
+};
+
+enum
+{
+ REF_ENTRY_TYPE_INHIBIT,
+ REF_ENTRY_TYPE_THROTTLE
+};
+
+static DBusObjectPathVTable
+gs_listener_vtable = { &gs_listener_unregister_handler,
+ &gs_listener_message_handler,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSListener, gs_listener, G_TYPE_OBJECT)
+
+GQuark
+gs_listener_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ {
+ quark = g_quark_from_static_string ("gs_listener_error");
+ }
+
+ return quark;
+}
+
+static void
+gs_listener_ref_entry_free (GSListenerRefEntry *entry)
+{
+ g_free (entry->connection);
+ g_free (entry->application);
+ g_free (entry->reason);
+ g_free (entry);
+ entry = NULL;
+}
+
+static void
+gs_listener_unregister_handler (DBusConnection *connection,
+ void *data)
+{
+}
+
+static gboolean
+send_dbus_message (DBusConnection *connection,
+ DBusMessage *message)
+{
+ gboolean is_connected;
+ gboolean sent;
+
+ g_return_val_if_fail (message != NULL, FALSE);
+
+ if (! connection)
+ {
+ gs_debug ("There is no valid connection to the message bus");
+ return FALSE;
+ }
+
+ is_connected = dbus_connection_get_is_connected (connection);
+ if (! is_connected)
+ {
+ gs_debug ("Not connected to the message bus");
+ return FALSE;
+ }
+
+ sent = dbus_connection_send (connection, message, NULL);
+
+ return sent;
+}
+
+static void
+send_dbus_boolean_signal (GSListener *listener,
+ const char *name,
+ gboolean value)
+{
+ DBusMessage *message;
+ DBusMessageIter iter;
+
+ g_return_if_fail (listener != NULL);
+
+ message = dbus_message_new_signal (GS_LISTENER_PATH,
+ GS_LISTENER_SERVICE,
+ name);
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &value);
+
+ if (! send_dbus_message (listener->priv->connection, message))
+ {
+ gs_debug ("Could not send %s signal", name);
+ }
+
+ dbus_message_unref (message);
+}
+
+static void
+gs_listener_send_signal_active_changed (GSListener *listener)
+{
+ g_return_if_fail (listener != NULL);
+
+ gs_debug ("Sending the ActiveChanged(%s) signal on the session bus",
+ listener->priv->active ? "TRUE" : "FALSE");
+
+ send_dbus_boolean_signal (listener, "ActiveChanged", listener->priv->active);
+}
+
+static const char *
+get_name_for_entry_type (int entry_type)
+{
+ const char *name;
+
+ switch (entry_type)
+ {
+ case REF_ENTRY_TYPE_INHIBIT:
+ name = "inhibitor";
+ break;
+ case REF_ENTRY_TYPE_THROTTLE:
+ name = "throttler";
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return name;
+}
+
+static GHashTable *
+get_hash_for_entry_type (GSListener *listener,
+ int entry_type)
+{
+ GHashTable *hash;
+
+ switch (entry_type)
+ {
+ case REF_ENTRY_TYPE_INHIBIT:
+ hash = listener->priv->inhibitors;
+ break;
+ case REF_ENTRY_TYPE_THROTTLE:
+ hash = listener->priv->throttlers;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return hash;
+}
+
+static void
+list_ref_entry (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSListenerRefEntry *entry;
+
+ entry = (GSListenerRefEntry *)value;
+
+ gs_debug ("%s: %s for reason: %s",
+ get_name_for_entry_type (entry->entry_type),
+ entry->application,
+ entry->reason);
+}
+
+static gboolean
+listener_ref_entry_is_present (GSListener *listener,
+ int entry_type)
+{
+ guint n_entries;
+ gboolean is_set;
+ GHashTable *hash;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ /* if we aren't inhibited then activate */
+ n_entries = 0;
+ if (hash != NULL)
+ {
+ n_entries = g_hash_table_size (hash);
+
+ g_hash_table_foreach (hash, list_ref_entry, NULL);
+ }
+
+ is_set = (n_entries > 0);
+
+ return is_set;
+}
+
+static gboolean
+listener_check_activation (GSListener *listener)
+{
+ gboolean inhibited;
+ gboolean res;
+
+ gs_debug ("Checking for activation");
+
+ if (! listener->priv->activation_enabled)
+ {
+ return TRUE;
+ }
+
+ if (! listener->priv->session_idle)
+ {
+ return TRUE;
+ }
+
+ /* if we aren't inhibited then activate */
+ inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT);
+
+ res = FALSE;
+ if (! inhibited)
+ {
+ gs_debug ("Trying to activate");
+ res = gs_listener_set_active (listener, TRUE);
+ }
+
+ return res;
+}
+
+static void
+gs_listener_set_throttle (GSListener *listener,
+ gboolean throttled)
+{
+ g_return_if_fail (GS_IS_LISTENER (listener));
+
+ if (listener->priv->throttled != throttled)
+ {
+ gs_debug ("Changing throttle status: %d", throttled);
+
+ listener->priv->throttled = throttled;
+
+ g_signal_emit (listener, signals [THROTTLE_CHANGED], 0, throttled);
+ }
+}
+
+static gboolean
+listener_check_throttle (GSListener *listener)
+{
+ gboolean throttled;
+
+ gs_debug ("Checking for throttle");
+
+ throttled = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_THROTTLE);
+
+ if (throttled != listener->priv->throttled)
+ {
+ gs_listener_set_throttle (listener, throttled);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+listener_set_session_idle_internal (GSListener *listener,
+ gboolean idle)
+{
+ listener->priv->session_idle = idle;
+
+ if (idle)
+ {
+ listener->priv->session_idle_start = time (NULL);
+ }
+ else
+ {
+ listener->priv->session_idle_start = 0;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+listener_set_active_internal (GSListener *listener,
+ gboolean active)
+{
+ listener->priv->active = active;
+
+ /* if idle not in sync with active, change it */
+ if (listener->priv->session_idle != active)
+ {
+ listener_set_session_idle_internal (listener, active);
+ }
+
+ if (active)
+ {
+ listener->priv->active_start = time (NULL);
+ }
+ else
+ {
+ listener->priv->active_start = 0;
+ }
+
+ gs_listener_send_signal_active_changed (listener);
+
+ return TRUE;
+}
+
+gboolean
+gs_listener_set_active (GSListener *listener,
+ gboolean active)
+{
+ gboolean res;
+
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ if (listener->priv->active == active)
+ {
+ gs_debug ("Trying to set active state when already: %s",
+ active ? "active" : "inactive");
+ return FALSE;
+ }
+
+ res = FALSE;
+ g_signal_emit (listener, signals [ACTIVE_CHANGED], 0, active, &res);
+ if (! res)
+ {
+ /* if the signal is not handled then we haven't changed state */
+ gs_debug ("Active-changed signal not handled");
+
+ /* clear the idle state */
+ if (active)
+ {
+ listener_set_session_idle_internal (listener, FALSE);
+ }
+
+ return FALSE;
+ }
+
+ listener_set_active_internal (listener, active);
+
+ return TRUE;
+}
+
+gboolean
+gs_listener_set_session_idle (GSListener *listener,
+ gboolean idle)
+{
+ gboolean res;
+
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ gs_debug ("Setting session idle: %d", idle);
+
+ if (listener->priv->session_idle == idle)
+ {
+ gs_debug ("Trying to set idle state when already %s",
+ idle ? "idle" : "not idle");
+ return FALSE;
+ }
+
+ if (idle)
+ {
+ gboolean inhibited;
+
+ inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT);
+
+ /* if we are inhibited then do nothing */
+ if (inhibited)
+ {
+ return FALSE;
+ }
+ }
+
+ listener->priv->session_idle = idle;
+ res = listener_check_activation (listener);
+
+ /* if activation fails then don't set idle */
+ if (res)
+ {
+ listener_set_session_idle_internal (listener, idle);
+ }
+ else
+ {
+ gs_debug ("Idle activation failed");
+ listener->priv->session_idle = !idle;
+ }
+
+ return res;
+}
+
+gboolean
+gs_listener_get_activation_enabled (GSListener *listener)
+{
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ return listener->priv->activation_enabled;
+}
+
+gboolean
+gs_listener_is_inhibited (GSListener *listener)
+{
+ gboolean inhibited;
+
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT);
+
+ return inhibited;
+}
+
+void
+gs_listener_set_activation_enabled (GSListener *listener,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_LISTENER (listener));
+
+ if (listener->priv->activation_enabled != enabled)
+ {
+ listener->priv->activation_enabled = enabled;
+ }
+}
+
+static dbus_bool_t
+listener_property_set_bool (GSListener *listener,
+ guint prop_id,
+ dbus_bool_t value)
+{
+ dbus_bool_t ret;
+
+ ret = FALSE;
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ gs_listener_set_active (listener, value);
+ ret = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+raise_error (DBusConnection *connection,
+ DBusMessage *in_reply_to,
+ const char *error_name,
+ char *format, ...)
+{
+ char buf[512];
+ DBusMessage *reply;
+
+ va_list args;
+ va_start (args, format);
+ vsnprintf (buf, sizeof (buf), format, args);
+ va_end (args);
+
+ gs_debug (buf);
+ reply = dbus_message_new_error (in_reply_to, error_name, buf);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+}
+
+static void
+raise_syntax (DBusConnection *connection,
+ DBusMessage *in_reply_to,
+ const char *method_name)
+{
+ raise_error (connection, in_reply_to,
+ GS_LISTENER_SERVICE ".SyntaxError",
+ "There is a syntax error in the invocation of the method %s",
+ method_name);
+}
+
+static guint32
+generate_cookie (void)
+{
+ guint32 cookie;
+
+ cookie = (guint32)g_random_int_range (1, G_MAXINT32);
+
+ return cookie;
+}
+
+static guint32
+listener_generate_unique_key (GSListener *listener,
+ int entry_type)
+{
+ guint32 cookie;
+ GHashTable *hash;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ do
+ {
+ cookie = generate_cookie ();
+ }
+ while (g_hash_table_lookup (hash, &cookie) != NULL);
+
+ return cookie;
+}
+
+static void
+listener_ref_entry_check (GSListener *listener,
+ int entry_type)
+{
+ switch (entry_type)
+ {
+ case REF_ENTRY_TYPE_INHIBIT:
+ listener_check_activation (listener);
+ break;
+ case REF_ENTRY_TYPE_THROTTLE:
+ listener_check_throttle (listener);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+add_session_inhibit (GSListener *listener,
+ GSListenerRefEntry *entry)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter reply_iter;
+ DBusError error;
+ guint xid;
+ guint flags;
+
+ g_return_if_fail (listener != NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (SESSION_NAME,
+ SESSION_PATH,
+ SESSION_INTERFACE,
+ "Inhibit");
+ if (message == NULL)
+ {
+ gs_debug ("Couldn't allocate the dbus message");
+ return;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ xid = 0;
+ flags = 8;
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &entry->application);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &xid);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &entry->reason);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &flags);
+
+ /* FIXME: use async? */
+ reply = dbus_connection_send_with_reply_and_block (listener->priv->connection,
+ message,
+ -1,
+ &error);
+ dbus_message_unref (message);
+
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("%s raised:\n %s\n\n", error.name, error.message);
+ dbus_error_free (&error);
+ return;
+ }
+
+ dbus_message_iter_init (reply, &reply_iter);
+ dbus_message_iter_get_basic (&reply_iter, &entry->foreign_cookie);
+
+ dbus_message_unref (reply);
+}
+
+static void
+remove_session_inhibit (GSListener *listener,
+ GSListenerRefEntry *entry)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError error;
+
+ g_return_if_fail (listener != NULL);
+
+ if (entry->foreign_cookie == 0)
+ {
+ gs_debug ("Can't remove inhibitor from session: Session cookie not set");
+ return;
+ }
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (SESSION_NAME,
+ SESSION_PATH,
+ SESSION_INTERFACE,
+ "Uninhibit");
+ if (message == NULL)
+ {
+ gs_debug ("Couldn't allocate the dbus message");
+ return;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &entry->foreign_cookie);
+
+ /* FIXME: use async? */
+ reply = dbus_connection_send_with_reply_and_block (listener->priv->connection,
+ message,
+ -1,
+ &error);
+ dbus_message_unref (message);
+
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("%s raised:\n %s\n\n", error.name, error.message);
+ dbus_error_free (&error);
+ return;
+ }
+
+ dbus_message_unref (reply);
+}
+
+static void
+listener_add_ref_entry (GSListener *listener,
+ int entry_type,
+ GSListenerRefEntry *entry)
+{
+ GHashTable *hash;
+
+ gs_debug ("adding %s from %s for reason '%s' on connection %s",
+ get_name_for_entry_type (entry_type),
+ entry->application,
+ entry->reason,
+ entry->connection);
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+ g_hash_table_insert (hash, &entry->cookie, entry);
+
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ /* proxy inhibit over to mate session */
+ add_session_inhibit (listener, entry);
+ }
+
+ listener_ref_entry_check (listener, entry_type);
+}
+
+static gboolean
+listener_remove_ref_entry (GSListener *listener,
+ int entry_type,
+ guint32 cookie)
+{
+ GHashTable *hash;
+ gboolean removed;
+ GSListenerRefEntry *entry;
+
+ removed = FALSE;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ entry = g_hash_table_lookup (hash, &cookie);
+ if (entry == NULL)
+ {
+ goto out;
+ }
+
+ gs_debug ("removing %s from %s for reason '%s' on connection %s",
+ get_name_for_entry_type (entry_type),
+ entry->application,
+ entry->reason,
+ entry->connection);
+
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ /* remove inhibit from mate session */
+ remove_session_inhibit (listener, entry);
+ }
+
+ removed = g_hash_table_remove (hash, &cookie);
+out:
+ if (removed)
+ {
+ listener_ref_entry_check (listener, entry_type);
+ }
+ else
+ {
+ gs_debug ("Cookie %u was not in the list!", cookie);
+ }
+
+ return removed;
+}
+
+#if GLIB_CHECK_VERSION(2,12,0)
+#define _g_time_val_to_iso8601(t) g_time_val_to_iso8601(t)
+#else
+/* copied from GLib */
+static gchar *
+_g_time_val_to_iso8601 (GTimeVal *time_)
+{
+ gchar *retval;
+
+ g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
+
+#define ISO_8601_LEN 21
+#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
+ retval = g_new0 (gchar, ISO_8601_LEN + 1);
+
+ strftime (retval, ISO_8601_LEN,
+ ISO_8601_FORMAT,
+ gmtime (&(time_->tv_sec)));
+
+ return retval;
+}
+#endif
+
+static void
+accumulate_ref_entry (gpointer key,
+ GSListenerRefEntry *entry,
+ DBusMessageIter *iter)
+{
+ char *description;
+ char *time;
+
+ time = _g_time_val_to_iso8601 (&entry->since);
+
+ description = g_strdup_printf ("Application=\"%s\"; Since=\"%s\"; Reason=\"%s\";",
+ entry->application,
+ time,
+ entry->reason);
+
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &description);
+
+ g_free (description);
+ g_free (time);
+}
+
+static DBusHandlerResult
+listener_dbus_get_ref_entries (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ GHashTable *hash;
+ DBusMessageIter iter;
+ DBusMessageIter iter_array;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_iter_init_append (reply, &iter);
+ dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &iter_array);
+
+ if (hash != NULL)
+ {
+ g_hash_table_foreach (hash,
+ (GHFunc)accumulate_ref_entry,
+ &iter_array);
+ }
+
+ dbus_message_iter_close_container (&iter, &iter_array);
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+listener_add_ck_ref_entry (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message,
+ guint32 *cookiep)
+{
+ GSListenerRefEntry *entry;
+
+ entry = g_new0 (GSListenerRefEntry, 1);
+ entry->entry_type = entry_type;
+ entry->connection = g_strdup (dbus_message_get_sender (message));
+ entry->cookie = listener_generate_unique_key (listener, entry_type);
+ entry->application = g_strdup ("ConsoleKit");
+ entry->reason = g_strdup ("Session is not active");
+ g_get_current_time (&entry->since);
+
+ /* takes ownership of entry */
+ listener_add_ref_entry (listener, entry_type, entry);
+
+ if (cookiep != NULL)
+ {
+ *cookiep = entry->cookie;
+ }
+}
+
+static void
+listener_remove_ck_ref_entry (GSListener *listener,
+ int entry_type,
+ guint32 cookie)
+{
+ listener_remove_ref_entry (listener, entry_type, cookie);
+}
+
+static DBusHandlerResult
+listener_dbus_add_ref_entry (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ DBusError error;
+ const char *application;
+ const char *reason;
+ GSListenerRefEntry *entry;
+ DBusMessageIter iter;
+
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &application,
+ DBUS_TYPE_STRING, &reason,
+ DBUS_TYPE_INVALID))
+ {
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ raise_syntax (connection, message, "Inhibit");
+ }
+ else if (entry_type == REF_ENTRY_TYPE_THROTTLE)
+ {
+ raise_syntax (connection, message, "Throttle");
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ entry = g_new0 (GSListenerRefEntry, 1);
+ entry->entry_type = entry_type;
+ entry->connection = g_strdup (dbus_message_get_sender (message));
+ entry->cookie = listener_generate_unique_key (listener, entry_type);
+ entry->application = g_strdup (application);
+ entry->reason = g_strdup (reason);
+ g_get_current_time (&entry->since);
+
+ listener_add_ref_entry (listener, entry_type, entry);
+
+ dbus_message_iter_init_append (reply, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &entry->cookie);
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_dbus_remove_ref_entry (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ DBusError error;
+ const char *sender;
+ guint32 cookie;
+
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_UINT32, &cookie,
+ DBUS_TYPE_INVALID))
+ {
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ raise_syntax (connection, message, "UnInhibit");
+ }
+ else if (entry_type == REF_ENTRY_TYPE_THROTTLE)
+ {
+ raise_syntax (connection, message, "UnThrottle");
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ g_error ("No memory");
+
+ /* FIXME: check sender is from same connection as entry */
+ sender = dbus_message_get_sender (message);
+
+ listener_remove_ref_entry (listener, entry_type, cookie);
+
+ /* FIXME: Pointless? */
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static gboolean
+listener_ref_entry_remove_for_connection (GSListener *listener,
+ int entry_type,
+ const char *connection)
+{
+ gboolean removed;
+ GHashTable *hash;
+ GHashTableIter iter;
+ GSListenerRefEntry *entry;
+
+ if (connection == NULL)
+ return FALSE;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ removed = FALSE;
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
+ {
+ if (entry->connection != NULL &&
+ strcmp (connection, entry->connection) == 0)
+ {
+ gs_debug ("removing %s from %s for reason '%s' on connection %s",
+ get_name_for_entry_type (entry->entry_type),
+ entry->application,
+ entry->reason,
+ entry->connection);
+
+ if (entry->entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ /* remove inhibit from mate session */
+ remove_session_inhibit (listener, entry);
+ }
+
+ g_hash_table_iter_remove (&iter);
+ removed = TRUE;
+ }
+ }
+
+ return removed;
+}
+
+static void
+listener_service_deleted (GSListener *listener,
+ DBusMessage *message)
+{
+ const char *old_service_name;
+ const char *new_service_name;
+ gboolean removed;
+
+ if (! dbus_message_get_args (message, NULL,
+ DBUS_TYPE_STRING, &old_service_name,
+ DBUS_TYPE_STRING, &new_service_name,
+ DBUS_TYPE_INVALID))
+ {
+ g_error ("Invalid NameOwnerChanged signal from bus!");
+ return;
+ }
+
+ gs_debug ("DBUS service deleted: %s", new_service_name);
+
+ removed = listener_ref_entry_remove_for_connection (listener, REF_ENTRY_TYPE_THROTTLE, new_service_name);
+ if (removed)
+ {
+ listener_ref_entry_check (listener, REF_ENTRY_TYPE_THROTTLE);
+ }
+
+ removed = listener_ref_entry_remove_for_connection (listener, REF_ENTRY_TYPE_INHIBIT, new_service_name);
+ if (removed)
+ {
+ listener_ref_entry_check (listener, REF_ENTRY_TYPE_INHIBIT);
+ }
+
+}
+
+static void
+raise_property_type_error (DBusConnection *connection,
+ DBusMessage *in_reply_to,
+ const char *device_id)
+{
+ char buf [512];
+ DBusMessage *reply;
+
+ snprintf (buf, 511,
+ "Type mismatch setting property with id %s",
+ device_id);
+ gs_debug (buf);
+
+ reply = dbus_message_new_error (in_reply_to,
+ TYPE_MISMATCH_ERROR,
+ buf);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+listener_set_property (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message,
+ guint prop_id)
+{
+ const char *path;
+ int type;
+ gboolean rc;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ path = dbus_message_get_path (message);
+
+ dbus_message_iter_init (message, &iter);
+ type = dbus_message_iter_get_arg_type (&iter);
+ rc = FALSE;
+
+ switch (type)
+ {
+ case DBUS_TYPE_BOOLEAN:
+ {
+ dbus_bool_t v;
+ dbus_message_iter_get_basic (&iter, &v);
+ rc = listener_property_set_bool (listener, prop_id, v);
+ break;
+ }
+ default:
+ gs_debug ("Unsupported property type %d", type);
+ break;
+ }
+
+ if (! rc)
+ {
+ raise_property_type_error (connection, message, path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = dbus_message_new_method_return (message);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_get_property (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message,
+ guint prop_id)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (reply == NULL)
+ g_error ("No memory");
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ {
+ dbus_bool_t b;
+ b = listener->priv->active;
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);
+ }
+ break;
+ default:
+ gs_debug ("Unsupported property id %u", prop_id);
+ break;
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_get_active_time (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ dbus_uint32_t secs;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (listener->priv->active)
+ {
+ time_t now = time (NULL);
+
+ if (now < listener->priv->active_start)
+ {
+ /* shouldn't happen */
+ gs_debug ("Active start time is in the future");
+ secs = 0;
+ }
+ else if (listener->priv->active_start <= 0)
+ {
+ /* shouldn't happen */
+ gs_debug ("Active start time was not set");
+ secs = 0;
+ }
+ else
+ {
+ secs = now - listener->priv->active_start;
+ }
+ }
+ else
+ {
+ secs = 0;
+ }
+
+ gs_debug ("Returning screensaver active for %u seconds", secs);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &secs);
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_show_message (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ DBusError error;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (listener->priv->active)
+ {
+ char *summary;
+ char *body;
+ char *icon;
+
+ /* if we're not active we ignore the request */
+
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &summary,
+ DBUS_TYPE_STRING, &body,
+ DBUS_TYPE_STRING, &icon,
+ DBUS_TYPE_INVALID))
+ {
+ raise_syntax (connection, message, "ShowMessage");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ g_signal_emit (listener, signals [SHOW_MESSAGE], 0, summary, body, icon);
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+do_introspect (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_bool_t local_interface)
+{
+ DBusMessage *reply;
+ GString *xml;
+ char *xml_string;
+
+ /* standard header */
+ xml = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+ "<node>\n"
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " </interface>\n");
+
+ /* ScreenSaver interface */
+ xml = g_string_append (xml,
+ " <interface name=\"org.mate.ScreenSaver\">\n"
+ " <method name=\"Lock\">\n"
+ " </method>\n"
+ " <method name=\"Cycle\">\n"
+ " </method>\n"
+ " <method name=\"SimulateUserActivity\">\n"
+ " </method>\n"
+ " <method name=\"Inhibit\">\n"
+ " <arg name=\"application_name\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"cookie\" direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"UnInhibit\">\n"
+ " <arg name=\"cookie\" direction=\"in\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"GetInhibitors\">\n"
+ " <arg name=\"list\" direction=\"out\" type=\"as\"/>\n"
+ " </method>\n"
+ " <method name=\"Throttle\">\n"
+ " <arg name=\"application_name\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"cookie\" direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"UnThrottle\">\n"
+ " <arg name=\"cookie\" direction=\"in\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"GetActive\">\n"
+ " <arg name=\"value\" direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"GetActiveTime\">\n"
+ " <arg name=\"seconds\" direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"SetActive\">\n"
+ " <arg name=\"value\" direction=\"in\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"ShowMessage\">\n"
+ " <arg name=\"summary\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"body\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"icon\" direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <signal name=\"ActiveChanged\">\n"
+ " <arg name=\"new_value\" type=\"b\"/>\n"
+ " </signal>\n"
+ " </interface>\n");
+
+ reply = dbus_message_new_method_return (message);
+
+ xml = g_string_append (xml, "</node>\n");
+ xml_string = g_string_free (xml, FALSE);
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &xml_string,
+ DBUS_TYPE_INVALID);
+
+ g_free (xml_string);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_dbus_handle_session_message (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data,
+ dbus_bool_t local_interface)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+
+#if 0
+ g_message ("obj_path=%s interface=%s method=%s destination=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_destination (message));
+#endif
+
+ g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Lock"))
+ {
+ g_signal_emit (listener, signals [LOCK], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Quit"))
+ {
+ g_signal_emit (listener, signals [QUIT], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Cycle"))
+ {
+ g_signal_emit (listener, signals [CYCLE], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Inhibit"))
+ {
+ return listener_dbus_add_ref_entry (listener, REF_ENTRY_TYPE_INHIBIT, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "UnInhibit"))
+ {
+ return listener_dbus_remove_ref_entry (listener, REF_ENTRY_TYPE_INHIBIT, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetInhibitors"))
+ {
+ return listener_dbus_get_ref_entries (listener, REF_ENTRY_TYPE_INHIBIT, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Throttle"))
+ {
+ return listener_dbus_add_ref_entry (listener, REF_ENTRY_TYPE_THROTTLE, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "UnThrottle"))
+ {
+ return listener_dbus_remove_ref_entry (listener, REF_ENTRY_TYPE_THROTTLE, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "SetActive"))
+ {
+ return listener_set_property (listener, connection, message, PROP_ACTIVE);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetActive"))
+ {
+ return listener_get_property (listener, connection, message, PROP_ACTIVE);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetActiveTime"))
+ {
+ return listener_get_active_time (listener, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "ShowMessage"))
+ {
+ return listener_show_message (listener, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "SimulateUserActivity"))
+ {
+ g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, "org.freedesktop.DBus.Introspectable", "Introspect"))
+ {
+ return do_introspect (connection, message, local_interface);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean
+_listener_message_path_is_our_session (GSListener *listener,
+ DBusMessage *message)
+{
+ const char *ssid;
+ gboolean ours;
+
+ ours = FALSE;
+
+ ssid = dbus_message_get_path (message);
+ if (ssid != NULL
+ && listener->priv->session_id != NULL
+ && strcmp (ssid, listener->priv->session_id) == 0)
+ {
+ ours = TRUE;
+ }
+
+ return ours;
+}
+
+static DBusHandlerResult
+listener_dbus_handle_system_message (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data,
+ dbus_bool_t local_interface)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+
+ g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+#if 1
+ gs_debug ("obj_path=%s interface=%s method=%s destination=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_destination (message));
+#endif
+
+ if (dbus_message_is_signal (message, HAL_DEVICE_INTERFACE, "Condition"))
+ {
+ DBusError error;
+ const char *event;
+ const char *keyname;
+
+ dbus_error_init (&error);
+ if (dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &event,
+ DBUS_TYPE_STRING, &keyname,
+ DBUS_TYPE_INVALID))
+ {
+ if ((event && strcmp (event, "ButtonPressed") == 0) &&
+ (keyname && strcmp (keyname, "coffee") == 0))
+ {
+ gs_debug ("Coffee key was pressed - locking");
+ g_signal_emit (listener, signals [LOCK], 0);
+ }
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ dbus_error_free (&error);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Unlock"))
+ {
+ if (_listener_message_path_is_our_session (listener, message))
+ {
+ gs_debug ("Console kit requested session unlock");
+ gs_listener_set_active (listener, FALSE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Lock"))
+ {
+ if (_listener_message_path_is_our_session (listener, message))
+ {
+ gs_debug ("ConsoleKit requested session lock");
+ g_signal_emit (listener, signals [LOCK], 0);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "ActiveChanged"))
+ {
+ /* NB that `ActiveChanged' refers to the active
+ * session in ConsoleKit terminology - ie which
+ * session is currently displayed on the screen.
+ * mate-screensaver uses `active' to mean `is the
+ * screensaver active' (ie, is the screen locked) but
+ * that's not what we're referring to here.
+ */
+
+ if (_listener_message_path_is_our_session (listener, message))
+ {
+ DBusError error;
+ dbus_bool_t new_active;
+
+ dbus_error_init (&error);
+ if (dbus_message_get_args (message, &error,
+ DBUS_TYPE_BOOLEAN, &new_active,
+ DBUS_TYPE_INVALID))
+ {
+ gs_debug ("ConsoleKit notified ActiveChanged %d", new_active);
+
+ /* when we aren't active add an implicit throttle from CK
+ * when we become active remove the throttle and poke the lock */
+ if (new_active)
+ {
+ if (listener->priv->ck_throttle_cookie != 0)
+ {
+ listener_remove_ck_ref_entry (listener,
+ REF_ENTRY_TYPE_THROTTLE,
+ listener->priv->ck_throttle_cookie);
+ listener->priv->ck_throttle_cookie = 0;
+ }
+
+ g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
+ }
+ else
+ {
+ if (listener->priv->ck_throttle_cookie != 0)
+ {
+ g_warning ("ConsoleKit throttle already set");
+ listener_remove_ck_ref_entry (listener,
+ REF_ENTRY_TYPE_THROTTLE,
+ listener->priv->ck_throttle_cookie);
+ listener->priv->ck_throttle_cookie = 0;
+ }
+
+ listener_add_ck_ref_entry (listener,
+ REF_ENTRY_TYPE_THROTTLE,
+ connection,
+ message,
+ &listener->priv->ck_throttle_cookie);
+ }
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ dbus_error_free (&error);
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+gs_listener_message_handler (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+#if 0
+ g_message ("obj_path=%s interface=%s method=%s destination=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_destination (message));
+#endif
+
+ if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch"))
+ {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
+ strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0)
+ {
+ dbus_connection_unref (connection);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else
+ {
+ return listener_dbus_handle_session_message (connection, message, user_data, TRUE);
+ }
+}
+
+static gboolean
+gs_listener_dbus_init (GSListener *listener)
+{
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ if (listener->priv->connection == NULL)
+ {
+ listener->priv->connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
+ if (listener->priv->connection == NULL)
+ {
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("couldn't connect to session bus: %s",
+ error.message);
+ dbus_error_free (&error);
+ }
+ return FALSE;
+ }
+
+ dbus_connection_setup_with_g_main (listener->priv->connection, NULL);
+ dbus_connection_set_exit_on_disconnect (listener->priv->connection, FALSE);
+ }
+
+ if (listener->priv->system_connection == NULL)
+ {
+ listener->priv->system_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (listener->priv->system_connection == NULL)
+ {
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("couldn't connect to system bus: %s",
+ error.message);
+ dbus_error_free (&error);
+ }
+ return FALSE;
+ }
+
+ dbus_connection_setup_with_g_main (listener->priv->system_connection, NULL);
+ dbus_connection_set_exit_on_disconnect (listener->priv->system_connection, FALSE);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+reinit_dbus (GSListener *listener)
+{
+ gboolean initialized;
+ gboolean try_again;
+
+ initialized = gs_listener_dbus_init (listener);
+
+ /* if we didn't initialize then try again */
+ /* FIXME: Should we keep trying forever? If we fail more than
+ once or twice then the session bus may have died. The
+ problem is that if it is restarted it will likely have a
+ different bus address and we won't be able to find it */
+ try_again = !initialized;
+
+ return try_again;
+}
+
+static DBusHandlerResult
+listener_dbus_filter_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+ const char *path;
+
+ path = dbus_message_get_path (message);
+
+ /*
+ g_message ("obj_path=%s interface=%s method=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message));
+ */
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
+ && strcmp (path, DBUS_PATH_LOCAL) == 0)
+ {
+
+ g_message ("Got disconnected from the session message bus; "
+ "retrying to reconnect every 10 seconds");
+
+ dbus_connection_unref (connection);
+ listener->priv->connection = NULL;
+
+ g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener);
+ }
+ else if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+
+ if (listener->priv->inhibitors != NULL)
+ {
+ listener_service_deleted (listener, message);
+ }
+ }
+ else
+ {
+ return listener_dbus_handle_session_message (connection, message, user_data, FALSE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_dbus_system_filter_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+ const char *path;
+
+ path = dbus_message_get_path (message);
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
+ && strcmp (path, DBUS_PATH_LOCAL) == 0)
+ {
+
+ g_message ("Got disconnected from the system message bus; "
+ "retrying to reconnect every 10 seconds");
+
+ dbus_connection_unref (connection);
+ listener->priv->system_connection = NULL;
+
+ g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener);
+ }
+ else
+ {
+ return listener_dbus_handle_system_message (connection, message, user_data, FALSE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+gs_listener_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSListener *self;
+
+ self = GS_LISTENER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ gs_listener_set_active (self, g_value_get_boolean (value));
+ break;
+ case PROP_SESSION_IDLE:
+ gs_listener_set_session_idle (self, g_value_get_boolean (value));
+ break;
+ case PROP_ACTIVATION_ENABLED:
+ gs_listener_set_activation_enabled (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_listener_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSListener *self;
+
+ self = GS_LISTENER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, self->priv->active);
+ break;
+ case PROP_SESSION_IDLE:
+ g_value_set_boolean (value, self->priv->session_idle);
+ break;
+ case PROP_ACTIVATION_ENABLED:
+ g_value_set_boolean (value, self->priv->activation_enabled);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_listener_class_init (GSListenerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_listener_finalize;
+ object_class->get_property = gs_listener_get_property;
+ object_class->set_property = gs_listener_set_property;
+
+ signals [LOCK] =
+ g_signal_new ("lock",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, lock),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [QUIT] =
+ g_signal_new ("quit",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, quit),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [CYCLE] =
+ g_signal_new ("cycle",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, cycle),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [SIMULATE_USER_ACTIVITY] =
+ g_signal_new ("simulate-user-activity",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, simulate_user_activity),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [ACTIVE_CHANGED] =
+ g_signal_new ("active-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, active_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1,
+ G_TYPE_BOOLEAN);
+ signals [THROTTLE_CHANGED] =
+ g_signal_new ("throttle-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, throttle_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+ signals [SHOW_MESSAGE] =
+ g_signal_new ("show-message",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, show_message),
+ NULL,
+ NULL,
+ gs_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ g_object_class_install_property (object_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_ACTIVATION_ENABLED,
+ g_param_spec_boolean ("activation-enabled",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (GSListenerPrivate));
+}
+
+
+static gboolean
+screensaver_is_running (DBusConnection *connection)
+{
+ DBusError error;
+ gboolean exists;
+
+ g_return_val_if_fail (connection != NULL, FALSE);
+
+ dbus_error_init (&error);
+ exists = dbus_bus_name_has_owner (connection, GS_LISTENER_SERVICE, &error);
+ if (dbus_error_is_set (&error))
+ {
+ dbus_error_free (&error);
+ }
+
+ return exists;
+}
+
+gboolean
+gs_listener_acquire (GSListener *listener,
+ GError **error)
+{
+ gboolean acquired;
+ DBusError buserror;
+ gboolean is_connected;
+
+ g_return_val_if_fail (listener != NULL, FALSE);
+
+ if (! listener->priv->connection)
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ _("failed to register with the message bus"));
+ return FALSE;
+ }
+
+ is_connected = dbus_connection_get_is_connected (listener->priv->connection);
+ if (! is_connected)
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ _("not connected to the message bus"));
+ return FALSE;
+ }
+
+ if (screensaver_is_running (listener->priv->connection))
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ _("screensaver already running in this session"));
+ return FALSE;
+ }
+
+ dbus_error_init (&buserror);
+
+ if (dbus_connection_register_object_path (listener->priv->connection,
+ GS_LISTENER_PATH,
+ &gs_listener_vtable,
+ listener) == FALSE)
+ {
+ g_critical ("out of memory registering object path");
+ return FALSE;
+ }
+
+ acquired = dbus_bus_request_name (listener->priv->connection,
+ GS_LISTENER_SERVICE,
+ 0, &buserror) != -1;
+ if (dbus_error_is_set (&buserror))
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ buserror.message);
+ }
+
+ dbus_error_free (&buserror);
+
+ dbus_connection_add_filter (listener->priv->connection, listener_dbus_filter_function, listener, NULL);
+
+ dbus_bus_add_match (listener->priv->connection,
+ "type='signal'"
+ ",interface='"DBUS_INTERFACE_DBUS"'"
+ ",sender='"DBUS_SERVICE_DBUS"'"
+ ",member='NameOwnerChanged'",
+ NULL);
+
+ if (listener->priv->system_connection != NULL)
+ {
+ dbus_connection_add_filter (listener->priv->system_connection,
+ listener_dbus_system_filter_function,
+ listener,
+ NULL);
+
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"HAL_DEVICE_INTERFACE"'"
+ ",member='Condition'",
+ NULL);
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"CK_SESSION_INTERFACE"'"
+ ",member='Unlock'",
+ NULL);
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"CK_SESSION_INTERFACE"'"
+ ",member='Lock'",
+ NULL);
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"CK_SESSION_INTERFACE"'"
+ ",member='ActiveChanged'",
+ NULL);
+ }
+
+ return acquired;
+}
+
+static char *
+query_session_id (GSListener *listener)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+ DBusMessageIter reply_iter;
+ char *ssid;
+
+ if (listener->priv->system_connection == NULL)
+ {
+ gs_debug ("No connection to the system bus");
+ return NULL;
+ }
+
+ ssid = NULL;
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (CK_NAME, CK_MANAGER_PATH, CK_MANAGER_INTERFACE, "GetCurrentSession");
+ if (message == NULL)
+ {
+ gs_debug ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ /* FIXME: use async? */
+ reply = dbus_connection_send_with_reply_and_block (listener->priv->system_connection,
+ message,
+ -1, &error);
+ dbus_message_unref (message);
+
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("%s raised:\n %s\n\n", error.name, error.message);
+ dbus_error_free (&error);
+ return NULL;
+ }
+
+ dbus_message_iter_init (reply, &reply_iter);
+ dbus_message_iter_get_basic (&reply_iter, &ssid);
+
+ dbus_message_unref (reply);
+
+ return g_strdup (ssid);
+}
+
+static void
+init_session_id (GSListener *listener)
+{
+ g_free (listener->priv->session_id);
+ listener->priv->session_id = query_session_id (listener);
+ gs_debug ("Got session-id: %s", listener->priv->session_id);
+}
+
+static void
+gs_listener_init (GSListener *listener)
+{
+ listener->priv = GS_LISTENER_GET_PRIVATE (listener);
+
+ gs_listener_dbus_init (listener);
+
+ init_session_id (listener);
+
+ listener->priv->inhibitors = g_hash_table_new_full (g_int_hash,
+ g_int_equal,
+ NULL,
+ (GDestroyNotify)gs_listener_ref_entry_free);
+ listener->priv->throttlers = g_hash_table_new_full (g_int_hash,
+ g_int_equal,
+ NULL,
+ (GDestroyNotify)gs_listener_ref_entry_free);
+}
+
+static void
+gs_listener_finalize (GObject *object)
+{
+ GSListener *listener;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_LISTENER (object));
+
+ listener = GS_LISTENER (object);
+
+ g_return_if_fail (listener->priv != NULL);
+
+ if (listener->priv->inhibitors)
+ {
+ g_hash_table_destroy (listener->priv->inhibitors);
+ }
+
+ if (listener->priv->throttlers)
+ {
+ g_hash_table_destroy (listener->priv->throttlers);
+ }
+
+ g_free (listener->priv->session_id);
+
+ G_OBJECT_CLASS (gs_listener_parent_class)->finalize (object);
+}
+
+GSListener *
+gs_listener_new (void)
+{
+ GSListener *listener;
+
+ listener = g_object_new (GS_TYPE_LISTENER, NULL);
+
+ return GS_LISTENER (listener);
+}
diff --git a/src/gs-listener-dbus.c.orig b/src/gs-listener-dbus.c.orig
new file mode 100644
index 0000000..b8d4fab
--- /dev/null
+++ b/src/gs-listener-dbus.c.orig
@@ -0,0 +1,2272 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+
+#include <glib/gi18n.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gs-listener-dbus.h"
+#include "gs-marshal.h"
+#include "gs-debug.h"
+
+/* this is for dbus < 0.3 */
+#if ((DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 30))
+#define dbus_bus_name_has_owner(connection, name, err) dbus_bus_service_exists(connection, name, err)
+#define dbus_bus_request_name(connection, name, flags, err) dbus_bus_acquire_service(connection, name, flags, err)
+#endif
+
+static void gs_listener_class_init (GSListenerClass *klass);
+static void gs_listener_init (GSListener *listener);
+static void gs_listener_finalize (GObject *object);
+
+static void gs_listener_unregister_handler (DBusConnection *connection,
+ void *data);
+
+static DBusHandlerResult gs_listener_message_handler (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data);
+
+#define GS_LISTENER_SERVICE "org.mate.ScreenSaver"
+#define GS_LISTENER_PATH "/org/mate/ScreenSaver"
+#define GS_LISTENER_INTERFACE "org.mate.ScreenSaver"
+
+#define HAL_DEVICE_INTERFACE "org.freedesktop.Hal.Device"
+
+#define CK_NAME "org.freedesktop.ConsoleKit"
+#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
+
+#define SESSION_NAME "org.mate.SessionManager"
+#define SESSION_PATH "/org/mate/SessionManager"
+#define SESSION_INTERFACE "org.mate.SessionManager"
+
+#define TYPE_MISMATCH_ERROR GS_LISTENER_INTERFACE ".TypeMismatch"
+
+#define GS_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_LISTENER, GSListenerPrivate))
+
+struct GSListenerPrivate
+{
+ DBusConnection *connection;
+ DBusConnection *system_connection;
+
+ guint session_idle : 1;
+ guint active : 1;
+ guint activation_enabled : 1;
+ guint throttled : 1;
+ GHashTable *inhibitors;
+ GHashTable *throttlers;
+ time_t active_start;
+ time_t session_idle_start;
+ char *session_id;
+
+ guint32 ck_throttle_cookie;
+};
+
+typedef struct
+{
+ int entry_type;
+ char *application;
+ char *reason;
+ char *connection;
+ guint32 cookie;
+ guint32 foreign_cookie;
+ GTimeVal since;
+} GSListenerRefEntry;
+
+enum
+{
+ LOCK,
+ CYCLE,
+ QUIT,
+ SIMULATE_USER_ACTIVITY,
+ ACTIVE_CHANGED,
+ THROTTLE_CHANGED,
+ SHOW_MESSAGE,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_ACTIVE,
+ PROP_SESSION_IDLE,
+ PROP_ACTIVATION_ENABLED,
+};
+
+enum
+{
+ REF_ENTRY_TYPE_INHIBIT,
+ REF_ENTRY_TYPE_THROTTLE
+};
+
+static DBusObjectPathVTable
+gs_listener_vtable = { &gs_listener_unregister_handler,
+ &gs_listener_message_handler,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSListener, gs_listener, G_TYPE_OBJECT)
+
+GQuark
+gs_listener_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ {
+ quark = g_quark_from_static_string ("gs_listener_error");
+ }
+
+ return quark;
+}
+
+static void
+gs_listener_ref_entry_free (GSListenerRefEntry *entry)
+{
+ g_free (entry->connection);
+ g_free (entry->application);
+ g_free (entry->reason);
+ g_free (entry);
+ entry = NULL;
+}
+
+static void
+gs_listener_unregister_handler (DBusConnection *connection,
+ void *data)
+{
+}
+
+static gboolean
+send_dbus_message (DBusConnection *connection,
+ DBusMessage *message)
+{
+ gboolean is_connected;
+ gboolean sent;
+
+ g_return_val_if_fail (message != NULL, FALSE);
+
+ if (! connection)
+ {
+ gs_debug ("There is no valid connection to the message bus");
+ return FALSE;
+ }
+
+ is_connected = dbus_connection_get_is_connected (connection);
+ if (! is_connected)
+ {
+ gs_debug ("Not connected to the message bus");
+ return FALSE;
+ }
+
+ sent = dbus_connection_send (connection, message, NULL);
+
+ return sent;
+}
+
+static void
+send_dbus_boolean_signal (GSListener *listener,
+ const char *name,
+ gboolean value)
+{
+ DBusMessage *message;
+ DBusMessageIter iter;
+
+ g_return_if_fail (listener != NULL);
+
+ message = dbus_message_new_signal (GS_LISTENER_PATH,
+ GS_LISTENER_SERVICE,
+ name);
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &value);
+
+ if (! send_dbus_message (listener->priv->connection, message))
+ {
+ gs_debug ("Could not send %s signal", name);
+ }
+
+ dbus_message_unref (message);
+}
+
+static void
+gs_listener_send_signal_active_changed (GSListener *listener)
+{
+ g_return_if_fail (listener != NULL);
+
+ gs_debug ("Sending the ActiveChanged(%s) signal on the session bus",
+ listener->priv->active ? "TRUE" : "FALSE");
+
+ send_dbus_boolean_signal (listener, "ActiveChanged", listener->priv->active);
+}
+
+static const char *
+get_name_for_entry_type (int entry_type)
+{
+ const char *name;
+
+ switch (entry_type)
+ {
+ case REF_ENTRY_TYPE_INHIBIT:
+ name = "inhibitor";
+ break;
+ case REF_ENTRY_TYPE_THROTTLE:
+ name = "throttler";
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return name;
+}
+
+static GHashTable *
+get_hash_for_entry_type (GSListener *listener,
+ int entry_type)
+{
+ GHashTable *hash;
+
+ switch (entry_type)
+ {
+ case REF_ENTRY_TYPE_INHIBIT:
+ hash = listener->priv->inhibitors;
+ break;
+ case REF_ENTRY_TYPE_THROTTLE:
+ hash = listener->priv->throttlers;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return hash;
+}
+
+static void
+list_ref_entry (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSListenerRefEntry *entry;
+
+ entry = (GSListenerRefEntry *)value;
+
+ gs_debug ("%s: %s for reason: %s",
+ get_name_for_entry_type (entry->entry_type),
+ entry->application,
+ entry->reason);
+}
+
+static gboolean
+listener_ref_entry_is_present (GSListener *listener,
+ int entry_type)
+{
+ guint n_entries;
+ gboolean is_set;
+ GHashTable *hash;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ /* if we aren't inhibited then activate */
+ n_entries = 0;
+ if (hash != NULL)
+ {
+ n_entries = g_hash_table_size (hash);
+
+ g_hash_table_foreach (hash, list_ref_entry, NULL);
+ }
+
+ is_set = (n_entries > 0);
+
+ return is_set;
+}
+
+static gboolean
+listener_check_activation (GSListener *listener)
+{
+ gboolean inhibited;
+ gboolean res;
+
+ gs_debug ("Checking for activation");
+
+ if (! listener->priv->activation_enabled)
+ {
+ return TRUE;
+ }
+
+ if (! listener->priv->session_idle)
+ {
+ return TRUE;
+ }
+
+ /* if we aren't inhibited then activate */
+ inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT);
+
+ res = FALSE;
+ if (! inhibited)
+ {
+ gs_debug ("Trying to activate");
+ res = gs_listener_set_active (listener, TRUE);
+ }
+
+ return res;
+}
+
+static void
+gs_listener_set_throttle (GSListener *listener,
+ gboolean throttled)
+{
+ g_return_if_fail (GS_IS_LISTENER (listener));
+
+ if (listener->priv->throttled != throttled)
+ {
+ gs_debug ("Changing throttle status: %d", throttled);
+
+ listener->priv->throttled = throttled;
+
+ g_signal_emit (listener, signals [THROTTLE_CHANGED], 0, throttled);
+ }
+}
+
+static gboolean
+listener_check_throttle (GSListener *listener)
+{
+ gboolean throttled;
+
+ gs_debug ("Checking for throttle");
+
+ throttled = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_THROTTLE);
+
+ if (throttled != listener->priv->throttled)
+ {
+ gs_listener_set_throttle (listener, throttled);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+listener_set_session_idle_internal (GSListener *listener,
+ gboolean idle)
+{
+ listener->priv->session_idle = idle;
+
+ if (idle)
+ {
+ listener->priv->session_idle_start = time (NULL);
+ }
+ else
+ {
+ listener->priv->session_idle_start = 0;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+listener_set_active_internal (GSListener *listener,
+ gboolean active)
+{
+ listener->priv->active = active;
+
+ /* if idle not in sync with active, change it */
+ if (listener->priv->session_idle != active)
+ {
+ listener_set_session_idle_internal (listener, active);
+ }
+
+ if (active)
+ {
+ listener->priv->active_start = time (NULL);
+ }
+ else
+ {
+ listener->priv->active_start = 0;
+ }
+
+ gs_listener_send_signal_active_changed (listener);
+
+ return TRUE;
+}
+
+gboolean
+gs_listener_set_active (GSListener *listener,
+ gboolean active)
+{
+ gboolean res;
+
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ if (listener->priv->active == active)
+ {
+ gs_debug ("Trying to set active state when already: %s",
+ active ? "active" : "inactive");
+ return FALSE;
+ }
+
+ res = FALSE;
+ g_signal_emit (listener, signals [ACTIVE_CHANGED], 0, active, &res);
+ if (! res)
+ {
+ /* if the signal is not handled then we haven't changed state */
+ gs_debug ("Active-changed signal not handled");
+
+ /* clear the idle state */
+ if (active)
+ {
+ listener_set_session_idle_internal (listener, FALSE);
+ }
+
+ return FALSE;
+ }
+
+ listener_set_active_internal (listener, active);
+
+ return TRUE;
+}
+
+gboolean
+gs_listener_set_session_idle (GSListener *listener,
+ gboolean idle)
+{
+ gboolean res;
+
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ gs_debug ("Setting session idle: %d", idle);
+
+ if (listener->priv->session_idle == idle)
+ {
+ gs_debug ("Trying to set idle state when already %s",
+ idle ? "idle" : "not idle");
+ return FALSE;
+ }
+
+ if (idle)
+ {
+ gboolean inhibited;
+
+ inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT);
+
+ /* if we are inhibited then do nothing */
+ if (inhibited)
+ {
+ return FALSE;
+ }
+ }
+
+ listener->priv->session_idle = idle;
+ res = listener_check_activation (listener);
+
+ /* if activation fails then don't set idle */
+ if (res)
+ {
+ listener_set_session_idle_internal (listener, idle);
+ }
+ else
+ {
+ gs_debug ("Idle activation failed");
+ listener->priv->session_idle = !idle;
+ }
+
+ return res;
+}
+
+gboolean
+gs_listener_get_activation_enabled (GSListener *listener)
+{
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ return listener->priv->activation_enabled;
+}
+
+gboolean
+gs_listener_is_inhibited (GSListener *listener)
+{
+ gboolean inhibited;
+
+ g_return_val_if_fail (GS_IS_LISTENER (listener), FALSE);
+
+ inhibited = listener_ref_entry_is_present (listener, REF_ENTRY_TYPE_INHIBIT);
+
+ return inhibited;
+}
+
+void
+gs_listener_set_activation_enabled (GSListener *listener,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_LISTENER (listener));
+
+ if (listener->priv->activation_enabled != enabled)
+ {
+ listener->priv->activation_enabled = enabled;
+ }
+}
+
+static dbus_bool_t
+listener_property_set_bool (GSListener *listener,
+ guint prop_id,
+ dbus_bool_t value)
+{
+ dbus_bool_t ret;
+
+ ret = FALSE;
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ gs_listener_set_active (listener, value);
+ ret = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+raise_error (DBusConnection *connection,
+ DBusMessage *in_reply_to,
+ const char *error_name,
+ char *format, ...)
+{
+ char buf[512];
+ DBusMessage *reply;
+
+ va_list args;
+ va_start (args, format);
+ vsnprintf (buf, sizeof (buf), format, args);
+ va_end (args);
+
+ gs_debug (buf);
+ reply = dbus_message_new_error (in_reply_to, error_name, buf);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+}
+
+static void
+raise_syntax (DBusConnection *connection,
+ DBusMessage *in_reply_to,
+ const char *method_name)
+{
+ raise_error (connection, in_reply_to,
+ GS_LISTENER_SERVICE ".SyntaxError",
+ "There is a syntax error in the invocation of the method %s",
+ method_name);
+}
+
+static guint32
+generate_cookie (void)
+{
+ guint32 cookie;
+
+ cookie = (guint32)g_random_int_range (1, G_MAXINT32);
+
+ return cookie;
+}
+
+static guint32
+listener_generate_unique_key (GSListener *listener,
+ int entry_type)
+{
+ guint32 cookie;
+ GHashTable *hash;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ do
+ {
+ cookie = generate_cookie ();
+ }
+ while (g_hash_table_lookup (hash, &cookie) != NULL);
+
+ return cookie;
+}
+
+static void
+listener_ref_entry_check (GSListener *listener,
+ int entry_type)
+{
+ switch (entry_type)
+ {
+ case REF_ENTRY_TYPE_INHIBIT:
+ listener_check_activation (listener);
+ break;
+ case REF_ENTRY_TYPE_THROTTLE:
+ listener_check_throttle (listener);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+add_session_inhibit (GSListener *listener,
+ GSListenerRefEntry *entry)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter reply_iter;
+ DBusError error;
+ guint xid;
+ guint flags;
+
+ g_return_if_fail (listener != NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (SESSION_NAME,
+ SESSION_PATH,
+ SESSION_INTERFACE,
+ "Inhibit");
+ if (message == NULL)
+ {
+ gs_debug ("Couldn't allocate the dbus message");
+ return;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ xid = 0;
+ flags = 8;
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &entry->application);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &xid);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &entry->reason);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &flags);
+
+ /* FIXME: use async? */
+ reply = dbus_connection_send_with_reply_and_block (listener->priv->connection,
+ message,
+ -1,
+ &error);
+ dbus_message_unref (message);
+
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("%s raised:\n %s\n\n", error.name, error.message);
+ dbus_error_free (&error);
+ return;
+ }
+
+ dbus_message_iter_init (reply, &reply_iter);
+ dbus_message_iter_get_basic (&reply_iter, &entry->foreign_cookie);
+
+ dbus_message_unref (reply);
+}
+
+static void
+remove_session_inhibit (GSListener *listener,
+ GSListenerRefEntry *entry)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusError error;
+
+ g_return_if_fail (listener != NULL);
+
+ if (entry->foreign_cookie == 0)
+ {
+ gs_debug ("Can't remove inhibitor from session: Session cookie not set");
+ return;
+ }
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (SESSION_NAME,
+ SESSION_PATH,
+ SESSION_INTERFACE,
+ "Uninhibit");
+ if (message == NULL)
+ {
+ gs_debug ("Couldn't allocate the dbus message");
+ return;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &entry->foreign_cookie);
+
+ /* FIXME: use async? */
+ reply = dbus_connection_send_with_reply_and_block (listener->priv->connection,
+ message,
+ -1,
+ &error);
+ dbus_message_unref (message);
+
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("%s raised:\n %s\n\n", error.name, error.message);
+ dbus_error_free (&error);
+ return;
+ }
+
+ dbus_message_unref (reply);
+}
+
+static void
+listener_add_ref_entry (GSListener *listener,
+ int entry_type,
+ GSListenerRefEntry *entry)
+{
+ GHashTable *hash;
+
+ gs_debug ("adding %s from %s for reason '%s' on connection %s",
+ get_name_for_entry_type (entry_type),
+ entry->application,
+ entry->reason,
+ entry->connection);
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+ g_hash_table_insert (hash, &entry->cookie, entry);
+
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ /* proxy inhibit over to mate session */
+ add_session_inhibit (listener, entry);
+ }
+
+ listener_ref_entry_check (listener, entry_type);
+}
+
+static gboolean
+listener_remove_ref_entry (GSListener *listener,
+ int entry_type,
+ guint32 cookie)
+{
+ GHashTable *hash;
+ gboolean removed;
+ GSListenerRefEntry *entry;
+
+ removed = FALSE;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ entry = g_hash_table_lookup (hash, &cookie);
+ if (entry == NULL)
+ {
+ goto out;
+ }
+
+ gs_debug ("removing %s from %s for reason '%s' on connection %s",
+ get_name_for_entry_type (entry_type),
+ entry->application,
+ entry->reason,
+ entry->connection);
+
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ /* remove inhibit from mate session */
+ remove_session_inhibit (listener, entry);
+ }
+
+ removed = g_hash_table_remove (hash, &cookie);
+out:
+ if (removed)
+ {
+ listener_ref_entry_check (listener, entry_type);
+ }
+ else
+ {
+ gs_debug ("Cookie %u was not in the list!", cookie);
+ }
+
+ return removed;
+}
+
+#if GLIB_CHECK_VERSION(2,12,0)
+#define _g_time_val_to_iso8601(t) g_time_val_to_iso8601(t)
+#else
+/* copied from GLib */
+static gchar *
+_g_time_val_to_iso8601 (GTimeVal *time_)
+{
+ gchar *retval;
+
+ g_return_val_if_fail (time_->tv_usec >= 0 && time_->tv_usec < G_USEC_PER_SEC, NULL);
+
+#define ISO_8601_LEN 21
+#define ISO_8601_FORMAT "%Y-%m-%dT%H:%M:%SZ"
+ retval = g_new0 (gchar, ISO_8601_LEN + 1);
+
+ strftime (retval, ISO_8601_LEN,
+ ISO_8601_FORMAT,
+ gmtime (&(time_->tv_sec)));
+
+ return retval;
+}
+#endif
+
+static void
+accumulate_ref_entry (gpointer key,
+ GSListenerRefEntry *entry,
+ DBusMessageIter *iter)
+{
+ char *description;
+ char *time;
+
+ time = _g_time_val_to_iso8601 (&entry->since);
+
+ description = g_strdup_printf ("Application=\"%s\"; Since=\"%s\"; Reason=\"%s\";",
+ entry->application,
+ time,
+ entry->reason);
+
+ dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &description);
+
+ g_free (description);
+ g_free (time);
+}
+
+static DBusHandlerResult
+listener_dbus_get_ref_entries (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ GHashTable *hash;
+ DBusMessageIter iter;
+ DBusMessageIter iter_array;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_iter_init_append (reply, &iter);
+ dbus_message_iter_open_container (&iter,
+ DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING,
+ &iter_array);
+
+ if (hash != NULL)
+ {
+ g_hash_table_foreach (hash,
+ (GHFunc)accumulate_ref_entry,
+ &iter_array);
+ }
+
+ dbus_message_iter_close_container (&iter, &iter_array);
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+listener_add_ck_ref_entry (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message,
+ guint32 *cookiep)
+{
+ GSListenerRefEntry *entry;
+
+ entry = g_new0 (GSListenerRefEntry, 1);
+ entry->entry_type = entry_type;
+ entry->connection = g_strdup (dbus_message_get_sender (message));
+ entry->cookie = listener_generate_unique_key (listener, entry_type);
+ entry->application = g_strdup ("ConsoleKit");
+ entry->reason = g_strdup ("Session is not active");
+ g_get_current_time (&entry->since);
+
+ /* takes ownership of entry */
+ listener_add_ref_entry (listener, entry_type, entry);
+
+ if (cookiep != NULL)
+ {
+ *cookiep = entry->cookie;
+ }
+}
+
+static void
+listener_remove_ck_ref_entry (GSListener *listener,
+ int entry_type,
+ guint32 cookie)
+{
+ listener_remove_ref_entry (listener, entry_type, cookie);
+}
+
+static DBusHandlerResult
+listener_dbus_add_ref_entry (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ DBusError error;
+ const char *application;
+ const char *reason;
+ GSListenerRefEntry *entry;
+ DBusMessageIter iter;
+
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &application,
+ DBUS_TYPE_STRING, &reason,
+ DBUS_TYPE_INVALID))
+ {
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ raise_syntax (connection, message, "Inhibit");
+ }
+ else if (entry_type == REF_ENTRY_TYPE_THROTTLE)
+ {
+ raise_syntax (connection, message, "Throttle");
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ entry = g_new0 (GSListenerRefEntry, 1);
+ entry->entry_type = entry_type;
+ entry->connection = g_strdup (dbus_message_get_sender (message));
+ entry->cookie = listener_generate_unique_key (listener, entry_type);
+ entry->application = g_strdup (application);
+ entry->reason = g_strdup (reason);
+ g_get_current_time (&entry->since);
+
+ listener_add_ref_entry (listener, entry_type, entry);
+
+ dbus_message_iter_init_append (reply, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &entry->cookie);
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_dbus_remove_ref_entry (GSListener *listener,
+ int entry_type,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessage *reply;
+ DBusError error;
+ const char *sender;
+ guint32 cookie;
+
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_UINT32, &cookie,
+ DBUS_TYPE_INVALID))
+ {
+ if (entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ raise_syntax (connection, message, "UnInhibit");
+ }
+ else if (entry_type == REF_ENTRY_TYPE_THROTTLE)
+ {
+ raise_syntax (connection, message, "UnThrottle");
+ }
+ else
+ {
+ g_assert_not_reached ();
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = dbus_message_new_method_return (message);
+ if (reply == NULL)
+ g_error ("No memory");
+
+ /* FIXME: check sender is from same connection as entry */
+ sender = dbus_message_get_sender (message);
+
+ listener_remove_ref_entry (listener, entry_type, cookie);
+
+ /* FIXME: Pointless? */
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static gboolean
+listener_ref_entry_remove_for_connection (GSListener *listener,
+ int entry_type,
+ const char *connection)
+{
+ gboolean removed;
+ GHashTable *hash;
+ GHashTableIter iter;
+ GSListenerRefEntry *entry;
+
+ if (connection == NULL)
+ return FALSE;
+
+ hash = get_hash_for_entry_type (listener, entry_type);
+
+ removed = FALSE;
+ g_hash_table_iter_init (&iter, hash);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&entry))
+ {
+ if (entry->connection != NULL &&
+ strcmp (connection, entry->connection) == 0)
+ {
+ gs_debug ("removing %s from %s for reason '%s' on connection %s",
+ get_name_for_entry_type (entry->entry_type),
+ entry->application,
+ entry->reason,
+ entry->connection);
+
+ if (entry->entry_type == REF_ENTRY_TYPE_INHIBIT)
+ {
+ /* remove inhibit from mate session */
+ remove_session_inhibit (listener, entry);
+ }
+
+ g_hash_table_iter_remove (&iter);
+ removed = TRUE;
+ }
+ }
+
+ return removed;
+}
+
+static void
+listener_service_deleted (GSListener *listener,
+ DBusMessage *message)
+{
+ const char *old_service_name;
+ const char *new_service_name;
+ gboolean removed;
+
+ if (! dbus_message_get_args (message, NULL,
+ DBUS_TYPE_STRING, &old_service_name,
+ DBUS_TYPE_STRING, &new_service_name,
+ DBUS_TYPE_INVALID))
+ {
+ g_error ("Invalid NameOwnerChanged signal from bus!");
+ return;
+ }
+
+ gs_debug ("DBUS service deleted: %s", new_service_name);
+
+ removed = listener_ref_entry_remove_for_connection (listener, REF_ENTRY_TYPE_THROTTLE, new_service_name);
+ if (removed)
+ {
+ listener_ref_entry_check (listener, REF_ENTRY_TYPE_THROTTLE);
+ }
+
+ removed = listener_ref_entry_remove_for_connection (listener, REF_ENTRY_TYPE_INHIBIT, new_service_name);
+ if (removed)
+ {
+ listener_ref_entry_check (listener, REF_ENTRY_TYPE_INHIBIT);
+ }
+
+}
+
+static void
+raise_property_type_error (DBusConnection *connection,
+ DBusMessage *in_reply_to,
+ const char *device_id)
+{
+ char buf [512];
+ DBusMessage *reply;
+
+ snprintf (buf, 511,
+ "Type mismatch setting property with id %s",
+ device_id);
+ gs_debug (buf);
+
+ reply = dbus_message_new_error (in_reply_to,
+ TYPE_MISMATCH_ERROR,
+ buf);
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+}
+
+static DBusHandlerResult
+listener_set_property (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message,
+ guint prop_id)
+{
+ const char *path;
+ int type;
+ gboolean rc;
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ path = dbus_message_get_path (message);
+
+ dbus_message_iter_init (message, &iter);
+ type = dbus_message_iter_get_arg_type (&iter);
+ rc = FALSE;
+
+ switch (type)
+ {
+ case DBUS_TYPE_BOOLEAN:
+ {
+ dbus_bool_t v;
+ dbus_message_iter_get_basic (&iter, &v);
+ rc = listener_property_set_bool (listener, prop_id, v);
+ break;
+ }
+ default:
+ gs_debug ("Unsupported property type %d", type);
+ break;
+ }
+
+ if (! rc)
+ {
+ raise_property_type_error (connection, message, path);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ reply = dbus_message_new_method_return (message);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_get_property (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message,
+ guint prop_id)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (reply == NULL)
+ g_error ("No memory");
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ {
+ dbus_bool_t b;
+ b = listener->priv->active;
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &b);
+ }
+ break;
+ default:
+ gs_debug ("Unsupported property id %u", prop_id);
+ break;
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_get_active_time (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ dbus_uint32_t secs;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (listener->priv->active)
+ {
+ time_t now = time (NULL);
+
+ if (now < listener->priv->active_start)
+ {
+ /* shouldn't happen */
+ gs_debug ("Active start time is in the future");
+ secs = 0;
+ }
+ else if (listener->priv->active_start <= 0)
+ {
+ /* shouldn't happen */
+ gs_debug ("Active start time was not set");
+ secs = 0;
+ }
+ else
+ {
+ secs = now - listener->priv->active_start;
+ }
+ }
+ else
+ {
+ secs = 0;
+ }
+
+ gs_debug ("Returning screensaver active for %u seconds", secs);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &secs);
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_show_message (GSListener *listener,
+ DBusConnection *connection,
+ DBusMessage *message)
+{
+ DBusMessageIter iter;
+ DBusMessage *reply;
+ DBusError error;
+
+ reply = dbus_message_new_method_return (message);
+
+ dbus_message_iter_init_append (reply, &iter);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (listener->priv->active)
+ {
+ char *summary;
+ char *body;
+ char *icon;
+
+ /* if we're not active we ignore the request */
+
+ dbus_error_init (&error);
+ if (! dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &summary,
+ DBUS_TYPE_STRING, &body,
+ DBUS_TYPE_STRING, &icon,
+ DBUS_TYPE_INVALID))
+ {
+ raise_syntax (connection, message, "ShowMessage");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ g_signal_emit (listener, signals [SHOW_MESSAGE], 0, summary, body, icon);
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+do_introspect (DBusConnection *connection,
+ DBusMessage *message,
+ dbus_bool_t local_interface)
+{
+ DBusMessage *reply;
+ GString *xml;
+ char *xml_string;
+
+ /* standard header */
+ xml = g_string_new ("<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
+ "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
+ "<node>\n"
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
+ " <method name=\"Introspect\">\n"
+ " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
+ " </method>\n"
+ " </interface>\n");
+
+ /* ScreenSaver interface */
+ xml = g_string_append (xml,
+ " <interface name=\"org.mate.ScreenSaver\">\n"
+ " <method name=\"Lock\">\n"
+ " </method>\n"
+ " <method name=\"Cycle\">\n"
+ " </method>\n"
+ " <method name=\"SimulateUserActivity\">\n"
+ " </method>\n"
+ " <method name=\"Inhibit\">\n"
+ " <arg name=\"application_name\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"cookie\" direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"UnInhibit\">\n"
+ " <arg name=\"cookie\" direction=\"in\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"GetInhibitors\">\n"
+ " <arg name=\"list\" direction=\"out\" type=\"as\"/>\n"
+ " </method>\n"
+ " <method name=\"Throttle\">\n"
+ " <arg name=\"application_name\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"reason\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"cookie\" direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"UnThrottle\">\n"
+ " <arg name=\"cookie\" direction=\"in\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"GetActive\">\n"
+ " <arg name=\"value\" direction=\"out\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"GetActiveTime\">\n"
+ " <arg name=\"seconds\" direction=\"out\" type=\"u\"/>\n"
+ " </method>\n"
+ " <method name=\"SetActive\">\n"
+ " <arg name=\"value\" direction=\"in\" type=\"b\"/>\n"
+ " </method>\n"
+ " <method name=\"ShowMessage\">\n"
+ " <arg name=\"summary\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"body\" direction=\"in\" type=\"s\"/>\n"
+ " <arg name=\"icon\" direction=\"in\" type=\"s\"/>\n"
+ " </method>\n"
+ " <signal name=\"ActiveChanged\">\n"
+ " <arg name=\"new_value\" type=\"b\"/>\n"
+ " </signal>\n"
+ " </interface>\n");
+
+ reply = dbus_message_new_method_return (message);
+
+ xml = g_string_append (xml, "</node>\n");
+ xml_string = g_string_free (xml, FALSE);
+
+ dbus_message_append_args (reply,
+ DBUS_TYPE_STRING, &xml_string,
+ DBUS_TYPE_INVALID);
+
+ g_free (xml_string);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_dbus_handle_session_message (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data,
+ dbus_bool_t local_interface)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+
+#if 0
+ g_message ("obj_path=%s interface=%s method=%s destination=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_destination (message));
+#endif
+
+ g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Lock"))
+ {
+ g_signal_emit (listener, signals [LOCK], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Quit"))
+ {
+ g_signal_emit (listener, signals [QUIT], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Cycle"))
+ {
+ g_signal_emit (listener, signals [CYCLE], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Inhibit"))
+ {
+ return listener_dbus_add_ref_entry (listener, REF_ENTRY_TYPE_INHIBIT, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "UnInhibit"))
+ {
+ return listener_dbus_remove_ref_entry (listener, REF_ENTRY_TYPE_INHIBIT, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetInhibitors"))
+ {
+ return listener_dbus_get_ref_entries (listener, REF_ENTRY_TYPE_INHIBIT, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "Throttle"))
+ {
+ return listener_dbus_add_ref_entry (listener, REF_ENTRY_TYPE_THROTTLE, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "UnThrottle"))
+ {
+ return listener_dbus_remove_ref_entry (listener, REF_ENTRY_TYPE_THROTTLE, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "SetActive"))
+ {
+ return listener_set_property (listener, connection, message, PROP_ACTIVE);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetActive"))
+ {
+ return listener_get_property (listener, connection, message, PROP_ACTIVE);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "GetActiveTime"))
+ {
+ return listener_get_active_time (listener, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "ShowMessage"))
+ {
+ return listener_show_message (listener, connection, message);
+ }
+ if (dbus_message_is_method_call (message, GS_LISTENER_SERVICE, "SimulateUserActivity"))
+ {
+ g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_method_call (message, "org.freedesktop.DBus.Introspectable", "Introspect"))
+ {
+ return do_introspect (connection, message, local_interface);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static gboolean
+_listener_message_path_is_our_session (GSListener *listener,
+ DBusMessage *message)
+{
+ const char *ssid;
+ gboolean ours;
+
+ ours = FALSE;
+
+ ssid = dbus_message_get_path (message);
+ if (ssid != NULL
+ && listener->priv->session_id != NULL
+ && strcmp (ssid, listener->priv->session_id) == 0)
+ {
+ ours = TRUE;
+ }
+
+ return ours;
+}
+
+static DBusHandlerResult
+listener_dbus_handle_system_message (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data,
+ dbus_bool_t local_interface)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+
+ g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+#if 1
+ gs_debug ("obj_path=%s interface=%s method=%s destination=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_destination (message));
+#endif
+
+ if (dbus_message_is_signal (message, HAL_DEVICE_INTERFACE, "Condition"))
+ {
+ DBusError error;
+ const char *event;
+ const char *keyname;
+
+ dbus_error_init (&error);
+ if (dbus_message_get_args (message, &error,
+ DBUS_TYPE_STRING, &event,
+ DBUS_TYPE_STRING, &keyname,
+ DBUS_TYPE_INVALID))
+ {
+ if ((event && strcmp (event, "ButtonPressed") == 0) &&
+ (keyname && strcmp (keyname, "coffee") == 0))
+ {
+ gs_debug ("Coffee key was pressed - locking");
+ g_signal_emit (listener, signals [LOCK], 0);
+ }
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ dbus_error_free (&error);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Unlock"))
+ {
+ if (_listener_message_path_is_our_session (listener, message))
+ {
+ gs_debug ("Console kit requested session unlock");
+ gs_listener_set_active (listener, FALSE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "Lock"))
+ {
+ if (_listener_message_path_is_our_session (listener, message))
+ {
+ gs_debug ("ConsoleKit requested session lock");
+ g_signal_emit (listener, signals [LOCK], 0);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, CK_SESSION_INTERFACE, "ActiveChanged"))
+ {
+ /* NB that `ActiveChanged' refers to the active
+ * session in ConsoleKit terminology - ie which
+ * session is currently displayed on the screen.
+ * mate-screensaver uses `active' to mean `is the
+ * screensaver active' (ie, is the screen locked) but
+ * that's not what we're referring to here.
+ */
+
+ if (_listener_message_path_is_our_session (listener, message))
+ {
+ DBusError error;
+ dbus_bool_t new_active;
+
+ dbus_error_init (&error);
+ if (dbus_message_get_args (message, &error,
+ DBUS_TYPE_BOOLEAN, &new_active,
+ DBUS_TYPE_INVALID))
+ {
+ gs_debug ("ConsoleKit notified ActiveChanged %d", new_active);
+
+ /* when we aren't active add an implicit throttle from CK
+ * when we become active remove the throttle and poke the lock */
+ if (new_active)
+ {
+ if (listener->priv->ck_throttle_cookie != 0)
+ {
+ listener_remove_ck_ref_entry (listener,
+ REF_ENTRY_TYPE_THROTTLE,
+ listener->priv->ck_throttle_cookie);
+ listener->priv->ck_throttle_cookie = 0;
+ }
+
+ g_signal_emit (listener, signals [SIMULATE_USER_ACTIVITY], 0);
+ }
+ else
+ {
+ if (listener->priv->ck_throttle_cookie != 0)
+ {
+ g_warning ("ConsoleKit throttle already set");
+ listener_remove_ck_ref_entry (listener,
+ REF_ENTRY_TYPE_THROTTLE,
+ listener->priv->ck_throttle_cookie);
+ listener->priv->ck_throttle_cookie = 0;
+ }
+
+ listener_add_ck_ref_entry (listener,
+ REF_ENTRY_TYPE_THROTTLE,
+ connection,
+ message,
+ &listener->priv->ck_throttle_cookie);
+ }
+ }
+
+ if (dbus_error_is_set (&error))
+ {
+ dbus_error_free (&error);
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult
+gs_listener_message_handler (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ g_return_val_if_fail (connection != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+ g_return_val_if_fail (message != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+
+#if 0
+ g_message ("obj_path=%s interface=%s method=%s destination=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_destination (message));
+#endif
+
+ if (dbus_message_is_method_call (message, "org.freedesktop.DBus", "AddMatch"))
+ {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return (message);
+
+ if (reply == NULL)
+ {
+ g_error ("No memory");
+ }
+
+ if (! dbus_connection_send (connection, reply, NULL))
+ {
+ g_error ("No memory");
+ }
+
+ dbus_message_unref (reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected") &&
+ strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0)
+ {
+ dbus_connection_unref (connection);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ else
+ {
+ return listener_dbus_handle_session_message (connection, message, user_data, TRUE);
+ }
+}
+
+static gboolean
+gs_listener_dbus_init (GSListener *listener)
+{
+ DBusError error;
+
+ dbus_error_init (&error);
+
+ if (listener->priv->connection == NULL)
+ {
+ listener->priv->connection = dbus_bus_get (DBUS_BUS_SESSION, &error);
+ if (listener->priv->connection == NULL)
+ {
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("couldn't connect to session bus: %s",
+ error.message);
+ dbus_error_free (&error);
+ }
+ return FALSE;
+ }
+
+ dbus_connection_setup_with_g_main (listener->priv->connection, NULL);
+ dbus_connection_set_exit_on_disconnect (listener->priv->connection, FALSE);
+ }
+
+ if (listener->priv->system_connection == NULL)
+ {
+ listener->priv->system_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (listener->priv->system_connection == NULL)
+ {
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("couldn't connect to system bus: %s",
+ error.message);
+ dbus_error_free (&error);
+ }
+ return FALSE;
+ }
+
+ dbus_connection_setup_with_g_main (listener->priv->system_connection, NULL);
+ dbus_connection_set_exit_on_disconnect (listener->priv->system_connection, FALSE);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+reinit_dbus (GSListener *listener)
+{
+ gboolean initialized;
+ gboolean try_again;
+
+ initialized = gs_listener_dbus_init (listener);
+
+ /* if we didn't initialize then try again */
+ /* FIXME: Should we keep trying forever? If we fail more than
+ once or twice then the session bus may have died. The
+ problem is that if it is restarted it will likely have a
+ different bus address and we won't be able to find it */
+ try_again = !initialized;
+
+ return try_again;
+}
+
+static DBusHandlerResult
+listener_dbus_filter_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+ const char *path;
+
+ path = dbus_message_get_path (message);
+
+ /*
+ g_message ("obj_path=%s interface=%s method=%s",
+ dbus_message_get_path (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message));
+ */
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
+ && strcmp (path, DBUS_PATH_LOCAL) == 0)
+ {
+
+ g_message ("Got disconnected from the session message bus; "
+ "retrying to reconnect every 10 seconds");
+
+ dbus_connection_unref (connection);
+ listener->priv->connection = NULL;
+
+ g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener);
+ }
+ else if (dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged"))
+ {
+
+ if (listener->priv->inhibitors != NULL)
+ {
+ listener_service_deleted (listener, message);
+ }
+ }
+ else
+ {
+ return listener_dbus_handle_session_message (connection, message, user_data, FALSE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+listener_dbus_system_filter_function (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GSListener *listener = GS_LISTENER (user_data);
+ const char *path;
+
+ path = dbus_message_get_path (message);
+
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected")
+ && strcmp (path, DBUS_PATH_LOCAL) == 0)
+ {
+
+ g_message ("Got disconnected from the system message bus; "
+ "retrying to reconnect every 10 seconds");
+
+ dbus_connection_unref (connection);
+ listener->priv->system_connection = NULL;
+
+ g_timeout_add (10000, (GSourceFunc)reinit_dbus, listener);
+ }
+ else
+ {
+ return listener_dbus_handle_system_message (connection, message, user_data, FALSE);
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void
+gs_listener_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSListener *self;
+
+ self = GS_LISTENER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ gs_listener_set_active (self, g_value_get_boolean (value));
+ break;
+ case PROP_SESSION_IDLE:
+ gs_listener_set_session_idle (self, g_value_get_boolean (value));
+ break;
+ case PROP_ACTIVATION_ENABLED:
+ gs_listener_set_activation_enabled (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_listener_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSListener *self;
+
+ self = GS_LISTENER (object);
+
+ switch (prop_id)
+ {
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, self->priv->active);
+ break;
+ case PROP_SESSION_IDLE:
+ g_value_set_boolean (value, self->priv->session_idle);
+ break;
+ case PROP_ACTIVATION_ENABLED:
+ g_value_set_boolean (value, self->priv->activation_enabled);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_listener_class_init (GSListenerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_listener_finalize;
+ object_class->get_property = gs_listener_get_property;
+ object_class->set_property = gs_listener_set_property;
+
+ signals [LOCK] =
+ g_signal_new ("lock",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, lock),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [QUIT] =
+ g_signal_new ("quit",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, quit),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [CYCLE] =
+ g_signal_new ("cycle",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, cycle),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [SIMULATE_USER_ACTIVITY] =
+ g_signal_new ("simulate-user-activity",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, simulate_user_activity),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [ACTIVE_CHANGED] =
+ g_signal_new ("active-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, active_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1,
+ G_TYPE_BOOLEAN);
+ signals [THROTTLE_CHANGED] =
+ g_signal_new ("throttle-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, throttle_changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_BOOLEAN);
+ signals [SHOW_MESSAGE] =
+ g_signal_new ("show-message",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSListenerClass, show_message),
+ NULL,
+ NULL,
+ gs_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_STRING,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+
+ g_object_class_install_property (object_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_ACTIVATION_ENABLED,
+ g_param_spec_boolean ("activation-enabled",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (GSListenerPrivate));
+}
+
+
+static gboolean
+screensaver_is_running (DBusConnection *connection)
+{
+ DBusError error;
+ gboolean exists;
+
+ g_return_val_if_fail (connection != NULL, FALSE);
+
+ dbus_error_init (&error);
+ exists = dbus_bus_name_has_owner (connection, GS_LISTENER_SERVICE, &error);
+ if (dbus_error_is_set (&error))
+ {
+ dbus_error_free (&error);
+ }
+
+ return exists;
+}
+
+gboolean
+gs_listener_acquire (GSListener *listener,
+ GError **error)
+{
+ gboolean acquired;
+ DBusError buserror;
+ gboolean is_connected;
+
+ g_return_val_if_fail (listener != NULL, FALSE);
+
+ if (! listener->priv->connection)
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ _("failed to register with the message bus"));
+ return FALSE;
+ }
+
+ is_connected = dbus_connection_get_is_connected (listener->priv->connection);
+ if (! is_connected)
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ _("not connected to the message bus"));
+ return FALSE;
+ }
+
+ if (screensaver_is_running (listener->priv->connection))
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ _("screensaver already running in this session"));
+ return FALSE;
+ }
+
+ dbus_error_init (&buserror);
+
+ if (dbus_connection_register_object_path (listener->priv->connection,
+ GS_LISTENER_PATH,
+ &gs_listener_vtable,
+ listener) == FALSE)
+ {
+ g_critical ("out of memory registering object path");
+ return FALSE;
+ }
+
+ acquired = dbus_bus_request_name (listener->priv->connection,
+ GS_LISTENER_SERVICE,
+ 0, &buserror) != -1;
+ if (dbus_error_is_set (&buserror))
+ {
+ g_set_error (error,
+ GS_LISTENER_ERROR,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ "%s",
+ buserror.message);
+ }
+
+ dbus_error_free (&buserror);
+
+ dbus_connection_add_filter (listener->priv->connection, listener_dbus_filter_function, listener, NULL);
+
+ dbus_bus_add_match (listener->priv->connection,
+ "type='signal'"
+ ",interface='"DBUS_INTERFACE_DBUS"'"
+ ",sender='"DBUS_SERVICE_DBUS"'"
+ ",member='NameOwnerChanged'",
+ NULL);
+
+ if (listener->priv->system_connection != NULL)
+ {
+ dbus_connection_add_filter (listener->priv->system_connection,
+ listener_dbus_system_filter_function,
+ listener,
+ NULL);
+
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"HAL_DEVICE_INTERFACE"'"
+ ",member='Condition'",
+ NULL);
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"CK_SESSION_INTERFACE"'"
+ ",member='Unlock'",
+ NULL);
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"CK_SESSION_INTERFACE"'"
+ ",member='Lock'",
+ NULL);
+ dbus_bus_add_match (listener->priv->system_connection,
+ "type='signal'"
+ ",interface='"CK_SESSION_INTERFACE"'"
+ ",member='ActiveChanged'",
+ NULL);
+ }
+
+ return acquired;
+}
+
+static char *
+query_session_id (GSListener *listener)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+ DBusMessageIter reply_iter;
+ char *ssid;
+
+ if (listener->priv->system_connection == NULL)
+ {
+ gs_debug ("No connection to the system bus");
+ return NULL;
+ }
+
+ ssid = NULL;
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (CK_NAME, CK_MANAGER_PATH, CK_MANAGER_INTERFACE, "GetCurrentSession");
+ if (message == NULL)
+ {
+ gs_debug ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ /* FIXME: use async? */
+ reply = dbus_connection_send_with_reply_and_block (listener->priv->system_connection,
+ message,
+ -1, &error);
+ dbus_message_unref (message);
+
+ if (dbus_error_is_set (&error))
+ {
+ gs_debug ("%s raised:\n %s\n\n", error.name, error.message);
+ dbus_error_free (&error);
+ return NULL;
+ }
+
+ dbus_message_iter_init (reply, &reply_iter);
+ dbus_message_iter_get_basic (&reply_iter, &ssid);
+
+ dbus_message_unref (reply);
+
+ return g_strdup (ssid);
+}
+
+static void
+init_session_id (GSListener *listener)
+{
+ g_free (listener->priv->session_id);
+ listener->priv->session_id = query_session_id (listener);
+ gs_debug ("Got session-id: %s", listener->priv->session_id);
+}
+
+static void
+gs_listener_init (GSListener *listener)
+{
+ listener->priv = GS_LISTENER_GET_PRIVATE (listener);
+
+ gs_listener_dbus_init (listener);
+
+ init_session_id (listener);
+
+ listener->priv->inhibitors = g_hash_table_new_full (g_int_hash,
+ g_int_equal,
+ NULL,
+ (GDestroyNotify)gs_listener_ref_entry_free);
+ listener->priv->throttlers = g_hash_table_new_full (g_int_hash,
+ g_int_equal,
+ NULL,
+ (GDestroyNotify)gs_listener_ref_entry_free);
+}
+
+static void
+gs_listener_finalize (GObject *object)
+{
+ GSListener *listener;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_LISTENER (object));
+
+ listener = GS_LISTENER (object);
+
+ g_return_if_fail (listener->priv != NULL);
+
+ if (listener->priv->inhibitors)
+ {
+ g_hash_table_destroy (listener->priv->inhibitors);
+ }
+
+ if (listener->priv->throttlers)
+ {
+ g_hash_table_destroy (listener->priv->throttlers);
+ }
+
+ g_free (listener->priv->session_id);
+
+ G_OBJECT_CLASS (gs_listener_parent_class)->finalize (object);
+}
+
+GSListener *
+gs_listener_new (void)
+{
+ GSListener *listener;
+
+ listener = g_object_new (GS_TYPE_LISTENER, NULL);
+
+ return GS_LISTENER (listener);
+}
diff --git a/src/gs-listener-dbus.h b/src/gs-listener-dbus.h
new file mode 100644
index 0000000..a2e298a
--- /dev/null
+++ b/src/gs-listener-dbus.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_LISTENER_H
+#define __GS_LISTENER_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_LISTENER (gs_listener_get_type ())
+#define GS_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_LISTENER, GSListener))
+#define GS_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_LISTENER, GSListenerClass))
+#define GS_IS_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_LISTENER))
+#define GS_IS_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_LISTENER))
+#define GS_LISTENER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_LISTENER, GSListenerClass))
+
+typedef struct GSListenerPrivate GSListenerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSListenerPrivate *priv;
+} GSListener;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* lock) (GSListener *listener);
+ void (* cycle) (GSListener *listener);
+ void (* quit) (GSListener *listener);
+ void (* simulate_user_activity) (GSListener *listener);
+ gboolean (* active_changed) (GSListener *listener,
+ gboolean active);
+ void (* throttle_changed) (GSListener *listener,
+ gboolean throttled);
+ void (* show_message) (GSListener *listener,
+ const char *summary,
+ const char *body,
+ const char *icon);
+
+} GSListenerClass;
+
+typedef enum
+{
+ GS_LISTENER_ERROR_SERVICE_UNAVAILABLE,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ GS_LISTENER_ERROR_ACTIVATION_FAILURE
+} GSListenerError;
+
+#define GS_LISTENER_ERROR gs_listener_error_quark ()
+
+GQuark gs_listener_error_quark (void);
+
+GType gs_listener_get_type (void);
+
+GSListener *gs_listener_new (void);
+gboolean gs_listener_acquire (GSListener *listener,
+ GError **error);
+gboolean gs_listener_set_active (GSListener *listener,
+ gboolean active);
+gboolean gs_listener_set_session_idle (GSListener *listener,
+ gboolean idle);
+void gs_listener_set_activation_enabled (GSListener *listener,
+ gboolean enabled);
+gboolean gs_listener_get_activation_enabled (GSListener *listener);
+gboolean gs_listener_is_inhibited (GSListener *listener);
+
+G_END_DECLS
+
+#endif /* __GS_LISTENER_H */
diff --git a/src/gs-listener-dbus.h.orig b/src/gs-listener-dbus.h.orig
new file mode 100644
index 0000000..40a7e99
--- /dev/null
+++ b/src/gs-listener-dbus.h.orig
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_LISTENER_H
+#define __GS_LISTENER_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_LISTENER (gs_listener_get_type ())
+#define GS_LISTENER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_LISTENER, GSListener))
+#define GS_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_LISTENER, GSListenerClass))
+#define GS_IS_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_LISTENER))
+#define GS_IS_LISTENER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_LISTENER))
+#define GS_LISTENER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_LISTENER, GSListenerClass))
+
+typedef struct GSListenerPrivate GSListenerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSListenerPrivate *priv;
+} GSListener;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* lock) (GSListener *listener);
+ void (* cycle) (GSListener *listener);
+ void (* quit) (GSListener *listener);
+ void (* simulate_user_activity) (GSListener *listener);
+ gboolean (* active_changed) (GSListener *listener,
+ gboolean active);
+ void (* throttle_changed) (GSListener *listener,
+ gboolean throttled);
+ void (* show_message) (GSListener *listener,
+ const char *summary,
+ const char *body,
+ const char *icon);
+
+} GSListenerClass;
+
+typedef enum
+{
+ GS_LISTENER_ERROR_SERVICE_UNAVAILABLE,
+ GS_LISTENER_ERROR_ACQUISITION_FAILURE,
+ GS_LISTENER_ERROR_ACTIVATION_FAILURE
+} GSListenerError;
+
+#define GS_LISTENER_ERROR gs_listener_error_quark ()
+
+GQuark gs_listener_error_quark (void);
+
+GType gs_listener_get_type (void);
+
+GSListener *gs_listener_new (void);
+gboolean gs_listener_acquire (GSListener *listener,
+ GError **error);
+gboolean gs_listener_set_active (GSListener *listener,
+ gboolean active);
+gboolean gs_listener_set_session_idle (GSListener *listener,
+ gboolean idle);
+void gs_listener_set_activation_enabled (GSListener *listener,
+ gboolean enabled);
+gboolean gs_listener_get_activation_enabled (GSListener *listener);
+gboolean gs_listener_is_inhibited (GSListener *listener);
+
+G_END_DECLS
+
+#endif /* __GS_LISTENER_H */
diff --git a/src/gs-lock-plug.c b/src/gs-lock-plug.c
new file mode 100644
index 0000000..81bea13
--- /dev/null
+++ b/src/gs-lock-plug.c
@@ -0,0 +1,2050 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <X11/XKBlib.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+#ifdef WITH_KBD_LAYOUT_INDICATOR
+#include <libmatekbd/matekbd-indicator.h>
+#endif
+
+#ifdef WITH_LIBMATENOTIFY
+#include <libmatenotify/notify.h>
+#endif
+
+#include "gs-lock-plug.h"
+
+#include "gs-debug.h"
+
+#define KEY_LOCK_DIALOG_THEME "/apps/mate-screensaver/lock_dialog_theme"
+#define MDM_FLEXISERVER_COMMAND "mdmflexiserver"
+#define MDM_FLEXISERVER_ARGS "--startnew Standard"
+
+/* same as SMS ;) */
+#define NOTE_BUFFER_MAX_CHARS 160
+
+enum
+{
+ AUTH_PAGE = 0,
+};
+
+#define FACE_ICON_SIZE 48
+#define DIALOG_TIMEOUT_MSEC 60000
+
+static void gs_lock_plug_finalize (GObject *object);
+
+#define GS_LOCK_PLUG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_LOCK_PLUG, GSLockPlugPrivate))
+
+struct GSLockPlugPrivate
+{
+ GtkWidget *vbox;
+ GtkWidget *auth_action_area;
+
+ GtkWidget *notebook;
+ GtkWidget *auth_face_image;
+ GtkWidget *auth_realname_label;
+ GtkWidget *auth_username_label;
+ GtkWidget *auth_prompt_label;
+ GtkWidget *auth_prompt_entry;
+ GtkWidget *auth_prompt_box;
+ GtkWidget *auth_capslock_label;
+ GtkWidget *auth_message_label;
+ GtkWidget *status_message_label;
+
+ GtkWidget *auth_unlock_button;
+ GtkWidget *auth_switch_button;
+ GtkWidget *auth_cancel_button;
+ GtkWidget *auth_logout_button;
+ GtkWidget *auth_note_button;
+ GtkWidget *note_tab;
+ GtkWidget *note_tab_label;
+ GtkWidget *note_text_view;
+ GtkWidget *note_ok_button;
+ GtkWidget *note_cancel_button;
+
+ GtkWidget *auth_prompt_kbd_layout_indicator;
+
+ gboolean caps_lock_on;
+ gboolean switch_enabled;
+ gboolean leave_note_enabled;
+ gboolean logout_enabled;
+ char *logout_command;
+ char *status_message;
+
+ guint timeout;
+
+ guint cancel_timeout_id;
+ guint auth_check_idle_id;
+ guint response_idle_id;
+
+ GList *key_events;
+};
+
+typedef struct _ResponseData ResponseData;
+
+struct _ResponseData
+{
+ gint response_id;
+};
+
+
+enum
+{
+ RESPONSE,
+ CLOSE,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_LOGOUT_ENABLED,
+ PROP_LOGOUT_COMMAND,
+ PROP_SWITCH_ENABLED,
+ PROP_STATUS_MESSAGE
+};
+
+static guint lock_plug_signals [LAST_SIGNAL];
+
+G_DEFINE_TYPE (GSLockPlug, gs_lock_plug, GTK_TYPE_PLUG)
+
+static void
+gs_lock_plug_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ GSLockPlug *plug;
+
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->style_set)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->style_set (widget, previous_style);
+ }
+
+ plug = GS_LOCK_PLUG (widget);
+
+ if (! plug->priv->vbox)
+ {
+ return;
+ }
+
+ gtk_container_set_border_width (GTK_CONTAINER (plug->priv->vbox), 12);
+ gtk_box_set_spacing (GTK_BOX (plug->priv->vbox), 12);
+
+ gtk_container_set_border_width (GTK_CONTAINER (plug->priv->auth_action_area), 0);
+ gtk_box_set_spacing (GTK_BOX (plug->priv->auth_action_area), 5);
+}
+
+static void
+do_user_switch (GSLockPlug *plug)
+{
+ GError *error;
+ gboolean res;
+ char *command;
+
+ command = g_strdup_printf ("%s %s",
+ MDM_FLEXISERVER_COMMAND,
+ MDM_FLEXISERVER_ARGS);
+
+ error = NULL;
+ res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (),
+ command,
+ &error);
+
+ g_free (command);
+
+ if (! res)
+ {
+ gs_debug ("Unable to start MDM greeter: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+set_status_text (GSLockPlug *plug,
+ const char *text)
+{
+ if (plug->priv->auth_message_label != NULL)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_message_label), text);
+ }
+}
+
+void
+gs_lock_plug_set_sensitive (GSLockPlug *plug,
+ gboolean sensitive)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, sensitive);
+ gtk_widget_set_sensitive (plug->priv->auth_action_area, sensitive);
+}
+
+static void
+remove_cancel_timeout (GSLockPlug *plug)
+{
+ if (plug->priv->cancel_timeout_id > 0)
+ {
+ g_source_remove (plug->priv->cancel_timeout_id);
+ plug->priv->cancel_timeout_id = 0;
+ }
+}
+
+static void
+remove_response_idle (GSLockPlug *plug)
+{
+ if (plug->priv->response_idle_id > 0)
+ {
+ g_source_remove (plug->priv->response_idle_id);
+ plug->priv->response_idle_id = 0;
+ }
+}
+
+static void
+gs_lock_plug_response (GSLockPlug *plug,
+ gint response_id)
+{
+ int new_response;
+
+ new_response = response_id;
+
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ /* Act only on response IDs we recognize */
+ if (!(response_id == GS_LOCK_PLUG_RESPONSE_OK
+ || response_id == GS_LOCK_PLUG_RESPONSE_CANCEL))
+ {
+ return;
+ }
+
+ remove_cancel_timeout (plug);
+ remove_response_idle (plug);
+
+ if (response_id == GS_LOCK_PLUG_RESPONSE_CANCEL)
+ {
+ gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), "");
+ }
+
+ g_signal_emit (plug,
+ lock_plug_signals [RESPONSE],
+ 0,
+ new_response);
+}
+
+static gboolean
+response_cancel_idle_cb (GSLockPlug *plug)
+{
+ plug->priv->response_idle_id = 0;
+
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+
+ return FALSE;
+}
+
+static gboolean
+dialog_timed_out (GSLockPlug *plug)
+{
+ gs_lock_plug_set_sensitive (plug, FALSE);
+ set_status_text (plug, _("Time has expired."));
+
+ if (plug->priv->response_idle_id != 0)
+ {
+ g_warning ("Response idle ID already set but shouldn't be");
+ }
+
+ remove_response_idle (plug);
+
+ plug->priv->response_idle_id = g_timeout_add (2000,
+ (GSourceFunc)response_cancel_idle_cb,
+ plug);
+ return FALSE;
+}
+
+
+static void
+capslock_update (GSLockPlug *plug,
+ gboolean is_on)
+{
+
+ plug->priv->caps_lock_on = is_on;
+
+ if (plug->priv->auth_capslock_label == NULL)
+ {
+ return;
+ }
+
+ if (is_on)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_capslock_label),
+ _("You have the Caps Lock key on."));
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_capslock_label),
+ "");
+ }
+}
+
+static gboolean
+is_capslock_on (void)
+{
+ XkbStateRec states;
+ Display *dsp;
+
+ dsp = GDK_DISPLAY ();
+ if (XkbGetState (dsp, XkbUseCoreKbd, &states) != Success)
+ {
+ return FALSE;
+ }
+
+ return (states.locked_mods & LockMask) != 0;
+}
+
+static void
+restart_cancel_timeout (GSLockPlug *plug)
+{
+ remove_cancel_timeout (plug);
+
+ plug->priv->cancel_timeout_id = g_timeout_add (plug->priv->timeout,
+ (GSourceFunc)dialog_timed_out,
+ plug);
+}
+
+void
+gs_lock_plug_get_text (GSLockPlug *plug,
+ char **text)
+{
+ const char *typed_text;
+ char *null_text;
+ char *local_text;
+
+ typed_text = gtk_entry_get_text (GTK_ENTRY (plug->priv->auth_prompt_entry));
+ local_text = g_locale_from_utf8 (typed_text, strlen (typed_text), NULL, NULL, NULL);
+
+ null_text = g_strnfill (strlen (typed_text) + 1, '\b');
+ gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), null_text);
+ gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), "");
+ g_free (null_text);
+
+ if (text != NULL)
+ {
+ *text = local_text;
+ }
+}
+
+typedef struct
+{
+ GSLockPlug *plug;
+ gint response_id;
+ GMainLoop *loop;
+ gboolean destroyed;
+} RunInfo;
+
+static void
+shutdown_loop (RunInfo *ri)
+{
+ if (g_main_loop_is_running (ri->loop))
+ g_main_loop_quit (ri->loop);
+}
+
+static void
+run_unmap_handler (GSLockPlug *plug,
+ gpointer data)
+{
+ RunInfo *ri = data;
+
+ shutdown_loop (ri);
+}
+
+static void
+run_response_handler (GSLockPlug *plug,
+ gint response_id,
+ gpointer data)
+{
+ RunInfo *ri;
+
+ ri = data;
+
+ ri->response_id = response_id;
+
+ shutdown_loop (ri);
+}
+
+static gint
+run_delete_handler (GSLockPlug *plug,
+ GdkEventAny *event,
+ gpointer data)
+{
+ RunInfo *ri = data;
+
+ shutdown_loop (ri);
+
+ return TRUE; /* Do not destroy */
+}
+
+static void
+run_destroy_handler (GSLockPlug *plug,
+ gpointer data)
+{
+ RunInfo *ri = data;
+
+ /* shutdown_loop will be called by run_unmap_handler */
+ ri->destroyed = TRUE;
+}
+
+/* adapted from GTK+ gtkdialog.c */
+int
+gs_lock_plug_run (GSLockPlug *plug)
+{
+ RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE };
+ gboolean was_modal;
+ gulong response_handler;
+ gulong unmap_handler;
+ gulong destroy_handler;
+ gulong delete_handler;
+
+ g_return_val_if_fail (GS_IS_LOCK_PLUG (plug), -1);
+
+ g_object_ref (plug);
+
+ was_modal = GTK_WINDOW (plug)->modal;
+ if (!was_modal)
+ {
+ gtk_window_set_modal (GTK_WINDOW (plug), TRUE);
+ }
+
+ if (!GTK_WIDGET_VISIBLE (plug))
+ {
+ gtk_widget_show (GTK_WIDGET (plug));
+ }
+
+ response_handler =
+ g_signal_connect (plug,
+ "response",
+ G_CALLBACK (run_response_handler),
+ &ri);
+
+ unmap_handler =
+ g_signal_connect (plug,
+ "unmap",
+ G_CALLBACK (run_unmap_handler),
+ &ri);
+
+ delete_handler =
+ g_signal_connect (plug,
+ "delete_event",
+ G_CALLBACK (run_delete_handler),
+ &ri);
+
+ destroy_handler =
+ g_signal_connect (plug,
+ "destroy",
+ G_CALLBACK (run_destroy_handler),
+ &ri);
+
+ ri.loop = g_main_loop_new (NULL, FALSE);
+
+ GDK_THREADS_LEAVE ();
+ g_main_loop_run (ri.loop);
+ GDK_THREADS_ENTER ();
+
+ g_main_loop_unref (ri.loop);
+
+ ri.loop = NULL;
+
+ if (!ri.destroyed)
+ {
+ if (! was_modal)
+ {
+ gtk_window_set_modal (GTK_WINDOW (plug), FALSE);
+ }
+
+ g_signal_handler_disconnect (plug, response_handler);
+ g_signal_handler_disconnect (plug, unmap_handler);
+ g_signal_handler_disconnect (plug, delete_handler);
+ g_signal_handler_disconnect (plug, destroy_handler);
+ }
+
+ g_object_unref (plug);
+
+ return ri.response_id;
+}
+
+
+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;
+}
+
+static void
+rounded_rectangle (cairo_t *cr,
+ gdouble aspect,
+ gdouble x,
+ gdouble y,
+ gdouble corner_radius,
+ gdouble width,
+ gdouble height)
+{
+ gdouble radius;
+ gdouble degrees;
+
+ radius = corner_radius / aspect;
+ degrees = G_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr,
+ x + width - radius,
+ y + radius,
+ radius,
+ -90 * degrees,
+ 0 * degrees);
+ cairo_arc (cr,
+ x + width - radius,
+ y + height - radius,
+ radius,
+ 0 * degrees,
+ 90 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + height - radius,
+ radius,
+ 90 * degrees,
+ 180 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + radius,
+ radius,
+ 180 * degrees,
+ 270 * degrees);
+ cairo_close_path (cr);
+}
+
+static void
+image_set_from_pixbuf (GtkImage *image,
+ GdkPixbuf *source)
+{
+ cairo_t *cr;
+ cairo_t *cr_mask;
+ cairo_surface_t *surface;
+ GdkPixmap *pixmap;
+ GdkPixmap *bitmask;
+ int w;
+ int h;
+ int frame_width;
+ double radius;
+ GdkColor color;
+ double r;
+ double g;
+ double b;
+
+ frame_width = 5;
+
+ w = gdk_pixbuf_get_width (source) + frame_width * 2;
+ h = gdk_pixbuf_get_height (source) + frame_width * 2;
+
+ radius = w / 10;
+
+ pixmap = gdk_pixmap_new (GTK_WIDGET (image)->window, w, h, -1);
+ bitmask = gdk_pixmap_new (GTK_WIDGET (image)->window, w, h, 1);
+
+ cr = gdk_cairo_create (pixmap);
+ cr_mask = gdk_cairo_create (bitmask);
+
+ /* setup mask */
+ cairo_rectangle (cr_mask, 0, 0, w, h);
+ cairo_set_operator (cr_mask, CAIRO_OPERATOR_CLEAR);
+ cairo_fill (cr_mask);
+
+ rounded_rectangle (cr_mask, 1.0, 0.5, 0.5, radius, w - 1, h - 1);
+ cairo_set_operator (cr_mask, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgb (cr_mask, 1, 1, 1);
+ cairo_fill (cr_mask);
+
+ color = GTK_WIDGET (image)->style->bg [GTK_STATE_NORMAL];
+ r = (float)color.red / 65535.0;
+ g = (float)color.green / 65535.0;
+ b = (float)color.blue / 65535.0;
+
+ /* set up image */
+ cairo_rectangle (cr, 0, 0, w, h);
+ cairo_set_source_rgb (cr, r, g, b);
+ cairo_fill (cr);
+
+ rounded_rectangle (cr,
+ 1.0,
+ frame_width + 0.5,
+ frame_width + 0.5,
+ radius,
+ w - frame_width * 2 - 1,
+ h - frame_width * 2 - 1);
+ 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);
+
+ gtk_image_set_from_pixmap (image, pixmap, bitmask);
+
+ cairo_surface_destroy (surface);
+
+ g_object_unref (bitmask);
+ g_object_unref (pixmap);
+
+ cairo_destroy (cr_mask);
+ cairo_destroy (cr);
+}
+
+
+static gboolean
+check_user_file (const gchar *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 (g_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 gboolean
+set_face_image (GSLockPlug *plug)
+{
+ GdkPixbuf *pixbuf;
+ const char *homedir;
+ char *path;
+ int icon_size = 96;
+ gsize user_max_file = 65536;
+ uid_t uid;
+
+ homedir = g_get_home_dir ();
+ uid = getuid ();
+
+ path = g_build_filename (homedir, ".face", NULL);
+
+ pixbuf = NULL;
+ if (check_user_file (path, uid, user_max_file, 0, 0))
+ {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (path,
+ icon_size,
+ icon_size,
+ NULL);
+ }
+
+ g_free (path);
+
+ if (pixbuf == NULL)
+ {
+ return FALSE;
+ }
+
+ image_set_from_pixbuf (GTK_IMAGE (plug->priv->auth_face_image), pixbuf);
+
+ g_object_unref (pixbuf);
+
+ return TRUE;
+}
+
+static void
+gs_lock_plug_show (GtkWidget *widget)
+{
+ GSLockPlug *plug = GS_LOCK_PLUG (widget);
+
+ gs_profile_start (NULL);
+
+ gs_profile_start ("parent");
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->show)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->show (widget);
+ }
+
+ gs_profile_end ("parent");
+
+
+ if (plug->priv->auth_face_image)
+ {
+ set_face_image (plug);
+ }
+
+ capslock_update (plug, is_capslock_on ());
+
+ restart_cancel_timeout (plug);
+
+ gs_profile_end (NULL);
+}
+
+static void
+gs_lock_plug_hide (GtkWidget *widget)
+{
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->hide)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->hide (widget);
+ }
+}
+
+static void
+queue_key_event (GSLockPlug *plug,
+ GdkEventKey *event)
+{
+ GdkEvent *saved_event;
+
+ saved_event = gdk_event_copy ((GdkEvent *)event);
+ plug->priv->key_events = g_list_prepend (plug->priv->key_events,
+ saved_event);
+}
+
+static void
+forward_key_events (GSLockPlug *plug)
+{
+ plug->priv->key_events = g_list_reverse (plug->priv->key_events);
+ while (plug->priv->key_events != NULL)
+ {
+ GdkEventKey *event = plug->priv->key_events->data;
+
+ gtk_window_propagate_key_event (GTK_WINDOW (plug), event);
+
+ gdk_event_free ((GdkEvent *)event);
+
+ plug->priv->key_events = g_list_delete_link (plug->priv->key_events,
+ plug->priv->key_events);
+ }
+}
+
+static void
+gs_lock_plug_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GSLockPlug *plug = GS_LOCK_PLUG (widget);
+ int mod_width;
+ int mod_height;
+
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->size_request)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->size_request (widget, requisition);
+ }
+
+ /* don't constrain size when themed */
+ if (plug->priv->vbox == NULL)
+ {
+ return;
+ }
+
+ mod_width = requisition->height * 1.3;
+ mod_height = requisition->width / 1.6;
+ if (requisition->width < mod_width)
+ {
+ /* if the dialog is tall fill out the width */
+ requisition->width = mod_width;
+ }
+ else if (requisition->height < mod_height)
+ {
+ /* if the dialog is wide fill out the height */
+ requisition->height = mod_height;
+ }
+}
+
+static void
+gs_lock_plug_set_logout_enabled (GSLockPlug *plug,
+ gboolean logout_enabled)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ if (plug->priv->logout_enabled == logout_enabled)
+ {
+ return;
+ }
+
+ plug->priv->logout_enabled = logout_enabled;
+ g_object_notify (G_OBJECT (plug), "logout-enabled");
+
+ if (plug->priv->auth_logout_button == NULL)
+ {
+ return;
+ }
+
+ if (logout_enabled)
+ {
+ gtk_widget_show (plug->priv->auth_logout_button);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_logout_button);
+ }
+}
+
+static void
+gs_lock_plug_set_logout_command (GSLockPlug *plug,
+ const char *command)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ g_free (plug->priv->logout_command);
+
+ if (command)
+ {
+ plug->priv->logout_command = g_strdup (command);
+ }
+ else
+ {
+ plug->priv->logout_command = NULL;
+ }
+}
+
+static gboolean
+is_program_in_path (const char *program)
+{
+ char *tmp = g_find_program_in_path (program);
+ if (tmp != NULL)
+ {
+ g_free (tmp);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static void
+gs_lock_plug_set_status_message (GSLockPlug *plug,
+ const char *status_message)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ g_free (plug->priv->status_message);
+ plug->priv->status_message = g_strdup (status_message);
+
+ if (plug->priv->status_message_label)
+ {
+ if (plug->priv->status_message)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->status_message_label),
+ plug->priv->status_message);
+ gtk_widget_show (plug->priv->status_message_label);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->status_message_label);
+ }
+ }
+}
+
+static void
+gs_lock_plug_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSLockPlug *self;
+
+ self = GS_LOCK_PLUG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOGOUT_ENABLED:
+ g_value_set_boolean (value, self->priv->logout_enabled);
+ break;
+ case PROP_LOGOUT_COMMAND:
+ g_value_set_string (value, self->priv->logout_command);
+ break;
+ case PROP_SWITCH_ENABLED:
+ g_value_set_boolean (value, self->priv->switch_enabled);
+ break;
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_lock_plug_set_switch_enabled (GSLockPlug *plug,
+ gboolean switch_enabled)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ if (plug->priv->switch_enabled == switch_enabled)
+ {
+ return;
+ }
+
+ plug->priv->switch_enabled = switch_enabled;
+ g_object_notify (G_OBJECT (plug), "switch-enabled");
+
+ if (plug->priv->auth_switch_button == NULL)
+ {
+ return;
+ }
+
+ if (switch_enabled)
+ {
+ gboolean found;
+ found = is_program_in_path (MDM_FLEXISERVER_COMMAND);
+ if (found)
+ {
+ gtk_widget_show (plug->priv->auth_switch_button);
+ }
+ else
+ {
+ gs_debug ("Waring: MDM flexiserver command not found: %s", MDM_FLEXISERVER_COMMAND);
+ gtk_widget_hide (plug->priv->auth_switch_button);
+ }
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_switch_button);
+ }
+}
+
+static void
+gs_lock_plug_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSLockPlug *self;
+
+ self = GS_LOCK_PLUG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOGOUT_ENABLED:
+ gs_lock_plug_set_logout_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOGOUT_COMMAND:
+ gs_lock_plug_set_logout_command (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS_MESSAGE:
+ gs_lock_plug_set_status_message (self, g_value_get_string (value));
+ break;
+ case PROP_SWITCH_ENABLED:
+ gs_lock_plug_set_switch_enabled (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_lock_plug_close (GSLockPlug *plug)
+{
+ /* Synthesize delete_event to close dialog. */
+
+ GtkWidget *widget = GTK_WIDGET (plug);
+ GdkEvent *event;
+
+ event = gdk_event_new (GDK_DELETE);
+ event->any.window = g_object_ref (widget->window);
+ event->any.send_event = TRUE;
+
+ gtk_main_do_event (event);
+ gdk_event_free (event);
+}
+
+static void
+gs_lock_plug_class_init (GSLockPlugClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkBindingSet *binding_set;
+
+ object_class->finalize = gs_lock_plug_finalize;
+ object_class->get_property = gs_lock_plug_get_property;
+ object_class->set_property = gs_lock_plug_set_property;
+
+ widget_class->style_set = gs_lock_plug_style_set;
+ widget_class->show = gs_lock_plug_show;
+ widget_class->hide = gs_lock_plug_hide;
+ widget_class->size_request = gs_lock_plug_size_request;
+
+ klass->close = gs_lock_plug_close;
+
+ g_type_class_add_private (klass, sizeof (GSLockPlugPrivate));
+
+ lock_plug_signals [RESPONSE] = g_signal_new ("response",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSLockPlugClass, response),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+ lock_plug_signals [CLOSE] = g_signal_new ("close",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GSLockPlugClass, close),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_ENABLED,
+ g_param_spec_boolean ("logout-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_COMMAND,
+ g_param_spec_string ("logout-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_STATUS_MESSAGE,
+ g_param_spec_string ("status-message",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_SWITCH_ENABLED,
+ g_param_spec_boolean ("switch-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ binding_set = gtk_binding_set_by_class (klass);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+ "close", 0);
+}
+
+static void
+clear_clipboards (GSLockPlug *plug)
+{
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (plug), GDK_SELECTION_PRIMARY);
+ gtk_clipboard_clear (clipboard);
+ gtk_clipboard_set_text (clipboard, "", -1);
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (plug), GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_clear (clipboard);
+ gtk_clipboard_set_text (clipboard, "", -1);
+}
+
+static void
+take_note (GtkButton *button,
+ GSLockPlug *plug)
+{
+ int page;
+
+ page = gtk_notebook_page_num (GTK_NOTEBOOK (plug->priv->notebook), plug->priv->note_tab);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), page);
+ /* this counts as activity so restart the timer */
+ restart_cancel_timeout (plug);
+}
+
+static void
+submit_note (GtkButton *button,
+ GSLockPlug *plug)
+{
+#ifdef WITH_LIBMATENOTIFY
+ char *text;
+ char summary[128];
+ char *escaped_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ time_t t;
+ struct tm *tmp;
+ NotifyNotification *note;
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), AUTH_PAGE);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view));
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+ text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ gtk_text_buffer_set_text (buffer, "", 0);
+ escaped_text = g_markup_escape_text (text, -1);
+
+ t = time (NULL);
+ tmp = localtime (&t);
+ strftime (summary, 128, "%X", tmp);
+
+ notify_init ("mate-screensaver-dialog");
+ note = notify_notification_new (summary, escaped_text, NULL, NULL);
+ notify_notification_set_timeout (note, NOTIFY_EXPIRES_NEVER);
+ notify_notification_show (note, NULL);
+ g_object_unref (note);
+
+ g_free (text);
+ g_free (escaped_text);
+
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+#endif /* WITH_LIBMATENOTIFY */
+}
+
+static void
+cancel_note (GtkButton *button,
+ GSLockPlug *plug)
+{
+ GtkTextBuffer *buffer;
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), AUTH_PAGE);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view));
+ gtk_text_buffer_set_text (buffer, "", 0);
+
+ /* this counts as activity so restart the timer */
+ restart_cancel_timeout (plug);
+
+ gtk_window_set_default (GTK_WINDOW (plug), plug->priv->auth_unlock_button);
+
+ clear_clipboards (plug);
+}
+
+static void
+logout_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+ char **argv = NULL;
+ GError *error = NULL;
+ gboolean res;
+
+ if (! plug->priv->logout_command)
+ {
+ return;
+ }
+
+ res = g_shell_parse_argv (plug->priv->logout_command, NULL, &argv, &error);
+
+ if (! res)
+ {
+ g_warning ("Could not parse logout command: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_spawn_async (g_get_home_dir (),
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ NULL,
+ &error);
+
+ g_strfreev (argv);
+
+ if (error)
+ {
+ g_warning ("Could not run logout command: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+void
+gs_lock_plug_set_busy (GSLockPlug *plug)
+{
+ GdkCursor *cursor;
+ GtkWidget *top_level;
+
+ top_level = gtk_widget_get_toplevel (GTK_WIDGET (plug));
+
+ cursor = gdk_cursor_new (GDK_WATCH);
+ gdk_window_set_cursor (top_level->window, cursor);
+ gdk_cursor_unref (cursor);
+}
+
+void
+gs_lock_plug_set_ready (GSLockPlug *plug)
+{
+ GdkCursor *cursor;
+ GtkWidget *top_level;
+
+ top_level = gtk_widget_get_toplevel (GTK_WIDGET (plug));
+
+ cursor = gdk_cursor_new (GDK_LEFT_PTR);
+ gdk_window_set_cursor (top_level->window, cursor);
+ gdk_cursor_unref (cursor);
+}
+
+void
+gs_lock_plug_enable_prompt (GSLockPlug *plug,
+ const char *message,
+ gboolean visible)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ gs_debug ("Setting prompt to: %s", message);
+
+ gtk_widget_set_sensitive (plug->priv->auth_unlock_button, TRUE);
+ gtk_widget_show (plug->priv->auth_unlock_button);
+ gtk_widget_grab_default (plug->priv->auth_unlock_button);
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_prompt_label), message);
+ gtk_widget_show (plug->priv->auth_prompt_label);
+ gtk_entry_set_visibility (GTK_ENTRY (plug->priv->auth_prompt_entry), visible);
+ gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, TRUE);
+ gtk_widget_show (plug->priv->auth_prompt_entry);
+
+ if (! GTK_WIDGET_HAS_FOCUS (plug->priv->auth_prompt_entry))
+ {
+ gtk_widget_grab_focus (plug->priv->auth_prompt_entry);
+ }
+
+ /* were there any key events sent to the plug while the
+ * entry wasnt ready? If so, forward them along
+ */
+ forward_key_events (plug);
+
+ restart_cancel_timeout (plug);
+}
+
+void
+gs_lock_plug_disable_prompt (GSLockPlug *plug)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ /* gtk_widget_hide (plug->priv->auth_prompt_entry); */
+ /* gtk_widget_hide (plug->priv->auth_prompt_label); */
+ gtk_widget_set_sensitive (plug->priv->auth_unlock_button, FALSE);
+ gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, FALSE);
+ /* gtk_widget_hide (plug->priv->auth_unlock_button); */
+
+ gtk_widget_grab_default (plug->priv->auth_cancel_button);
+}
+
+void
+gs_lock_plug_show_message (GSLockPlug *plug,
+ const char *message)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ set_status_text (plug, message ? message : "");
+}
+
+/* button press handler used to inhibit popup menu */
+static gint
+entry_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+entry_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ GSLockPlug *plug)
+{
+ gboolean capslock_on;
+
+ restart_cancel_timeout (plug);
+
+ capslock_on = is_capslock_on ();
+
+ if (capslock_on != plug->priv->caps_lock_on)
+ {
+ capslock_update (plug, capslock_on);
+ }
+
+ /* if the input widget is visible and ready for input
+ * then just carry on as usual
+ */
+ if (GTK_WIDGET_VISIBLE (plug->priv->auth_prompt_entry) &&
+ GTK_WIDGET_IS_SENSITIVE (plug->priv->auth_prompt_entry))
+ {
+ return FALSE;
+ }
+
+ if (strcmp (event->string, "") == 0)
+ {
+ return FALSE;
+ }
+
+ queue_key_event (plug, event);
+
+ return TRUE;
+}
+
+/* adapted from gtk_dialog_add_button */
+static GtkWidget *
+gs_lock_plug_add_button (GSLockPlug *plug,
+ GtkWidget *action_area,
+ const gchar *button_text)
+{
+ GtkWidget *button;
+
+ g_return_val_if_fail (GS_IS_LOCK_PLUG (plug), NULL);
+ g_return_val_if_fail (button_text != NULL, NULL);
+
+ button = gtk_button_new_from_stock (button_text);
+
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+
+ gtk_widget_show (button);
+
+ gtk_box_pack_end (GTK_BOX (action_area),
+ button,
+ FALSE, TRUE, 0);
+
+ return button;
+}
+
+static char *
+get_user_display_name (void)
+{
+ const char *name;
+ char *utf8_name;
+
+ name = g_get_real_name ();
+
+ if (name == NULL || strcmp (name, "Unknown") == 0)
+ {
+ name = g_get_user_name ();
+ }
+
+ utf8_name = NULL;
+
+ if (name != NULL)
+ {
+ utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
+ }
+
+ return utf8_name;
+}
+
+static char *
+get_user_name (void)
+{
+ const char *name;
+ char *utf8_name;
+
+ name = g_get_user_name ();
+
+ utf8_name = NULL;
+ if (name != NULL)
+ {
+ utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
+ }
+
+ return utf8_name;
+}
+
+static void
+create_page_one_buttons (GSLockPlug *plug)
+{
+
+ gs_profile_start ("page one buttons");
+
+ plug->priv->auth_switch_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ _("S_witch User..."));
+ gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (plug->priv->auth_action_area),
+ plug->priv->auth_switch_button,
+ TRUE);
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_switch_button), FALSE);
+ gtk_widget_set_no_show_all (plug->priv->auth_switch_button, TRUE);
+
+ plug->priv->auth_logout_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ _("Log _Out"));
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_logout_button), FALSE);
+ gtk_widget_set_no_show_all (plug->priv->auth_logout_button, TRUE);
+
+ plug->priv->auth_cancel_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ GTK_STOCK_CANCEL);
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_cancel_button), FALSE);
+
+ plug->priv->auth_unlock_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ _("_Unlock"));
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_unlock_button), FALSE);
+
+ gtk_window_set_default (GTK_WINDOW (plug), plug->priv->auth_unlock_button);
+
+ gs_profile_end ("page one buttons");
+}
+
+/* adapted from MDM */
+static char *
+expand_string (const char *text)
+{
+ GString *str;
+ const char *p;
+ char *username;
+ int i;
+ int n_chars;
+ struct utsname name;
+
+ str = g_string_sized_new (strlen (text));
+
+ p = text;
+ n_chars = g_utf8_strlen (text, -1);
+ i = 0;
+
+ while (i < n_chars)
+ {
+ gunichar ch;
+
+ ch = g_utf8_get_char (p);
+
+ /* Backslash commands */
+ if (ch == '\\')
+ {
+ p = g_utf8_next_char (p);
+ i++;
+ ch = g_utf8_get_char (p);
+
+ if (i >= n_chars || ch == '\0')
+ {
+ g_warning ("Unescaped \\ at end of text\n");
+ goto bail;
+ }
+ else if (ch == 'n')
+ {
+ g_string_append_unichar (str, '\n');
+ }
+ else
+ {
+ g_string_append_unichar (str, ch);
+ }
+ }
+ else if (ch == '%')
+ {
+ p = g_utf8_next_char (p);
+ i++;
+ ch = g_utf8_get_char (p);
+
+ if (i >= n_chars || ch == '\0')
+ {
+ g_warning ("Unescaped %% at end of text\n");
+ goto bail;
+ }
+
+ switch (ch)
+ {
+ case '%':
+ g_string_append (str, "%");
+ break;
+ case 'c':
+ /* clock */
+ break;
+ case 'd':
+ /* display */
+ g_string_append (str, g_getenv ("DISPLAY"));
+ break;
+ case 'h':
+ /* hostname */
+ g_string_append (str, g_get_host_name ());
+ break;
+ case 'm':
+ /* machine name */
+ uname (&name);
+ g_string_append (str, name.machine);
+ break;
+ case 'n':
+ /* nodename */
+ uname (&name);
+ g_string_append (str, name.nodename);
+ break;
+ case 'r':
+ /* release */
+ uname (&name);
+ g_string_append (str, name.release);
+ break;
+ case 'R':
+ /* Real name */
+ username = get_user_display_name ();
+ g_string_append (str, username);
+ g_free (username);
+ break;
+ case 's':
+ /* system name */
+ uname (&name);
+ g_string_append (str, name.sysname);
+ break;
+ case 'U':
+ /* Username */
+ username = get_user_name ();
+ g_string_append (str, username);
+ g_free (username);
+ break;
+ default:
+ if (ch < 127)
+ {
+ g_warning ("unknown escape code %%%c in text\n", (char)ch);
+ }
+ else
+ {
+ g_warning ("unknown escape code %%(U%x) in text\n", (int)ch);
+ }
+ }
+ }
+ else
+ {
+ g_string_append_unichar (str, ch);
+ }
+ p = g_utf8_next_char (p);
+ i++;
+ }
+
+bail:
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+expand_string_for_label (GtkWidget *label)
+{
+ const char *template;
+ char *str;
+
+ template = gtk_label_get_label (GTK_LABEL (label));
+ str = expand_string (template);
+ gtk_label_set_label (GTK_LABEL (label), str);
+ g_free (str);
+}
+
+static void
+create_page_one (GSLockPlug *plug)
+{
+ GtkWidget *align;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkWidget *hbox;
+ char *str;
+
+ gs_profile_start ("page one");
+
+ align = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_notebook_append_page (GTK_NOTEBOOK (plug->priv->notebook), align, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 12);
+ gtk_container_add (GTK_CONTAINER (align), vbox);
+
+ plug->priv->auth_face_image = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), plug->priv->auth_face_image, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_face_image), 0.5, 1.0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+
+ str = g_strdup ("<span size=\"x-large\">%R</span>");
+ plug->priv->auth_realname_label = gtk_label_new (str);
+ g_free (str);
+ expand_string_for_label (plug->priv->auth_realname_label);
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_realname_label), 0.5, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_realname_label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_realname_label, FALSE, FALSE, 0);
+
+ /* To translators: This expands to USERNAME on HOSTNAME */
+ str = g_strdup_printf ("<span size=\"small\">%s</span>", _("%U on %h"));
+ plug->priv->auth_username_label = gtk_label_new (str);
+ g_free (str);
+ expand_string_for_label (plug->priv->auth_username_label);
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_username_label), 0.5, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_username_label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_username_label, FALSE, FALSE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
+
+ plug->priv->auth_prompt_label = gtk_label_new_with_mnemonic (_("_Password:"));
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_prompt_label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), plug->priv->auth_prompt_label, FALSE, FALSE, 0);
+
+ plug->priv->auth_prompt_entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), plug->priv->auth_prompt_entry, TRUE, TRUE, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (plug->priv->auth_prompt_label),
+ plug->priv->auth_prompt_entry);
+
+ plug->priv->auth_capslock_label = gtk_label_new ("");
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_capslock_label), 0.5, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_capslock_label, FALSE, FALSE, 0);
+
+ /* Status text */
+
+ plug->priv->auth_message_label = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), plug->priv->auth_message_label,
+ FALSE, FALSE, 0);
+ /* Buttons */
+ plug->priv->auth_action_area = gtk_hbutton_box_new ();
+
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (plug->priv->auth_action_area),
+ GTK_BUTTONBOX_END);
+
+ gtk_box_pack_end (GTK_BOX (vbox), plug->priv->auth_action_area,
+ FALSE, TRUE, 0);
+ gtk_widget_show (plug->priv->auth_action_area);
+
+ create_page_one_buttons (plug);
+
+ gs_profile_end ("page one");
+}
+
+static void
+unlock_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_OK);
+}
+
+static void
+cancel_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+}
+
+static void
+switch_user_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+
+ remove_response_idle (plug);
+
+ gs_lock_plug_set_sensitive (plug, FALSE);
+
+ plug->priv->response_idle_id = g_timeout_add (2000,
+ (GSourceFunc)response_cancel_idle_cb,
+ plug);
+
+ gs_lock_plug_set_busy (plug);
+ do_user_switch (plug);
+}
+
+static char *
+get_dialog_theme_name (GSLockPlug *plug)
+{
+ char *name;
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+ name = mateconf_client_get_string (client, KEY_LOCK_DIALOG_THEME, NULL);
+ g_object_unref (client);
+
+ return name;
+}
+
+static gboolean
+load_theme (GSLockPlug *plug)
+{
+ char *theme;
+ char *filename;
+ char *gtkbuilder;
+ char *rc;
+ GtkBuilder *builder;
+ GtkWidget *lock_dialog;
+ GError *error=NULL;
+
+ theme = get_dialog_theme_name (plug);
+ if (theme == NULL)
+ {
+ return FALSE;
+ }
+
+ filename = g_strdup_printf ("lock-dialog-%s.ui", theme);
+ gtkbuilder = g_build_filename (GTKBUILDERDIR, filename, NULL);
+ g_free (filename);
+ if (! g_file_test (gtkbuilder, G_FILE_TEST_IS_REGULAR))
+ {
+ g_free (gtkbuilder);
+ g_free (theme);
+ return FALSE;
+ }
+
+ filename = g_strdup_printf ("lock-dialog-%s.gtkrc", theme);
+ g_free (theme);
+
+ rc = g_build_filename (GTKBUILDERDIR, filename, NULL);
+ g_free (filename);
+ if (g_file_test (rc, G_FILE_TEST_IS_REGULAR))
+ {
+ gtk_rc_parse (rc);
+ }
+ g_free (rc);
+
+ builder = gtk_builder_new();
+
+ if (!gtk_builder_add_from_file (builder,gtkbuilder,&error))
+ {
+ g_warning ("Couldn't load builder file '%s': %s", gtkbuilder, error->message);
+ g_error_free(error);
+ g_free (gtkbuilder);
+ return FALSE;
+ }
+ g_free (gtkbuilder);
+
+ lock_dialog = GTK_WIDGET (gtk_builder_get_object(builder, "lock-dialog"));
+ gtk_container_add (GTK_CONTAINER (plug), lock_dialog);
+
+ plug->priv->vbox = NULL;
+ plug->priv->notebook = GTK_WIDGET (gtk_builder_get_object(builder, "notebook"));
+
+ plug->priv->auth_face_image = GTK_WIDGET (gtk_builder_get_object(builder, "auth-face-image"));
+ plug->priv->auth_action_area = GTK_WIDGET (gtk_builder_get_object(builder, "auth-action-area"));
+ plug->priv->auth_realname_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-realname-label"));
+ plug->priv->auth_username_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-username-label"));
+ plug->priv->auth_prompt_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-label"));
+ plug->priv->auth_prompt_entry = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-entry"));
+ plug->priv->auth_prompt_box = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-box"));
+ plug->priv->auth_capslock_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-capslock-label"));
+ plug->priv->auth_message_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-status-label"));
+ plug->priv->auth_unlock_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-unlock-button"));
+ plug->priv->auth_cancel_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-cancel-button"));
+ plug->priv->auth_logout_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-logout-button"));
+ plug->priv->auth_switch_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-switch-button"));
+ plug->priv->auth_note_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-note-button"));
+ plug->priv->note_tab = GTK_WIDGET (gtk_builder_get_object(builder, "note-tab"));
+ plug->priv->note_tab_label = GTK_WIDGET (gtk_builder_get_object(builder, "note-tab-label"));
+ plug->priv->note_ok_button = GTK_WIDGET (gtk_builder_get_object(builder, "note-ok-button"));
+ plug->priv->note_text_view = GTK_WIDGET (gtk_builder_get_object(builder, "note-text-view"));
+ plug->priv->note_cancel_button = GTK_WIDGET (gtk_builder_get_object(builder, "note-cancel-button"));
+
+ /* Placeholder for the keyboard indicator */
+ plug->priv->auth_prompt_kbd_layout_indicator = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-kbd-layout-indicator"));
+ if (plug->priv->auth_logout_button != NULL)
+ {
+ gtk_widget_set_no_show_all (plug->priv->auth_logout_button, TRUE);
+ }
+ if (plug->priv->auth_switch_button != NULL)
+ {
+ gtk_widget_set_no_show_all (plug->priv->auth_switch_button, TRUE);
+ }
+ if (plug->priv->auth_note_button != NULL)
+ {
+ gtk_widget_set_no_show_all (plug->priv->auth_note_button, TRUE);
+ }
+
+ gtk_widget_show_all (lock_dialog);
+
+ plug->priv->status_message_label = GTK_WIDGET (gtk_builder_get_object(builder, "status-message-label"));
+
+ return TRUE;
+}
+
+static int
+delete_handler (GSLockPlug *plug,
+ GdkEventAny *event,
+ gpointer data)
+{
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+
+ return TRUE; /* Do not destroy */
+}
+
+static void
+on_note_text_buffer_changed (GtkTextBuffer *buffer,
+ GSLockPlug *plug)
+{
+ int len;
+
+ len = gtk_text_buffer_get_char_count (buffer);
+ if (len > NOTE_BUFFER_MAX_CHARS)
+ {
+ gtk_widget_set_sensitive (plug->priv->note_text_view, FALSE);
+ }
+}
+
+static void
+gs_lock_plug_init (GSLockPlug *plug)
+{
+ gs_profile_start (NULL);
+
+ plug->priv = GS_LOCK_PLUG_GET_PRIVATE (plug);
+
+ clear_clipboards (plug);
+
+#ifdef WITH_LIBMATENOTIFY
+ plug->priv->leave_note_enabled = TRUE;
+#else
+ plug->priv->leave_note_enabled = FALSE;
+#endif
+
+ if (! load_theme (plug))
+ {
+ gs_debug ("Unable to load theme!");
+
+ plug->priv->vbox = gtk_vbox_new (FALSE, 0);
+
+ gtk_container_add (GTK_CONTAINER (plug), plug->priv->vbox);
+
+ /* Notebook */
+
+ plug->priv->notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (plug->priv->notebook), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (plug->priv->notebook), FALSE);
+ gtk_box_pack_start (GTK_BOX (plug->priv->vbox), plug->priv->notebook, TRUE, TRUE, 0);
+
+ /* Page 1 */
+
+ create_page_one (plug);
+
+ gtk_widget_show_all (plug->priv->vbox);
+ }
+
+ if (plug->priv->note_text_view != NULL)
+ {
+ GtkTextBuffer *buffer;
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view));
+ g_signal_connect (buffer, "changed", G_CALLBACK (on_note_text_buffer_changed), plug);
+ }
+
+ /* Layout indicator */
+#ifdef WITH_KBD_LAYOUT_INDICATOR
+ if (plug->priv->auth_prompt_kbd_layout_indicator != NULL)
+ {
+ XklEngine *engine;
+
+ engine = xkl_engine_get_instance (GDK_DISPLAY ());
+ if (xkl_engine_get_num_groups (engine) > 1)
+ {
+ GtkWidget *layout_indicator;
+
+ layout_indicator = matekbd_indicator_new ();
+ matekbd_indicator_set_parent_tooltips (MATEKBD_INDICATOR (layout_indicator), TRUE);
+ gtk_box_pack_start (GTK_BOX (plug->priv->auth_prompt_kbd_layout_indicator),
+ layout_indicator,
+ FALSE,
+ FALSE,
+ 6);
+
+ gtk_widget_show_all (layout_indicator);
+ gtk_widget_show (plug->priv->auth_prompt_kbd_layout_indicator);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_prompt_kbd_layout_indicator);
+ }
+
+ g_object_unref (engine);
+ }
+#endif
+
+ if (plug->priv->auth_note_button != NULL)
+ {
+ if (plug->priv->leave_note_enabled)
+ {
+ gtk_widget_show_all (plug->priv->auth_note_button);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_note_button);
+ }
+ }
+ if (plug->priv->auth_switch_button != NULL)
+ {
+ if (plug->priv->switch_enabled)
+ {
+ gtk_widget_show_all (plug->priv->auth_switch_button);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_switch_button);
+ }
+ }
+
+ gtk_widget_grab_default (plug->priv->auth_unlock_button);
+
+ if (plug->priv->auth_username_label != NULL)
+ {
+ expand_string_for_label (plug->priv->auth_username_label);
+ }
+
+ if (plug->priv->auth_realname_label != NULL)
+ {
+ expand_string_for_label (plug->priv->auth_realname_label);
+ }
+
+ if (! plug->priv->logout_enabled || ! plug->priv->logout_command)
+ {
+ if (plug->priv->auth_logout_button != NULL)
+ {
+ gtk_widget_hide (plug->priv->auth_logout_button);
+ }
+ }
+
+ plug->priv->timeout = DIALOG_TIMEOUT_MSEC;
+
+ g_signal_connect (plug, "key_press_event",
+ G_CALLBACK (entry_key_press), plug);
+
+ /* button press handler used to inhibit popup menu */
+ g_signal_connect (plug->priv->auth_prompt_entry, "button_press_event",
+ G_CALLBACK (entry_button_press), NULL);
+ gtk_entry_set_activates_default (GTK_ENTRY (plug->priv->auth_prompt_entry), TRUE);
+ gtk_entry_set_visibility (GTK_ENTRY (plug->priv->auth_prompt_entry), FALSE);
+
+ g_signal_connect (plug->priv->auth_unlock_button, "clicked",
+ G_CALLBACK (unlock_button_clicked), plug);
+
+ g_signal_connect (plug->priv->auth_cancel_button, "clicked",
+ G_CALLBACK (cancel_button_clicked), plug);
+
+ if (plug->priv->status_message_label)
+ {
+ if (plug->priv->status_message)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->status_message_label),
+ plug->priv->status_message);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->status_message_label);
+ }
+ }
+
+ if (plug->priv->auth_switch_button != NULL)
+ {
+ g_signal_connect (plug->priv->auth_switch_button, "clicked",
+ G_CALLBACK (switch_user_button_clicked), plug);
+ }
+
+ if (plug->priv->auth_note_button != NULL)
+ {
+ g_signal_connect (plug->priv->auth_note_button, "clicked",
+ G_CALLBACK (take_note), plug);
+ g_signal_connect (plug->priv->note_ok_button, "clicked",
+ G_CALLBACK (submit_note), plug);
+ g_signal_connect (plug->priv->note_cancel_button, "clicked",
+ G_CALLBACK (cancel_note), plug);
+ }
+
+ if (plug->priv->note_tab_label != NULL)
+ {
+ expand_string_for_label (plug->priv->note_tab_label);
+ }
+
+ if (plug->priv->auth_logout_button != NULL)
+ {
+ g_signal_connect (plug->priv->auth_logout_button, "clicked",
+ G_CALLBACK (logout_button_clicked), plug);
+ }
+
+ g_signal_connect (plug, "delete_event", G_CALLBACK (delete_handler), NULL);
+
+ gs_profile_end (NULL);
+}
+
+static void
+gs_lock_plug_finalize (GObject *object)
+{
+ GSLockPlug *plug;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_LOCK_PLUG (object));
+
+ plug = GS_LOCK_PLUG (object);
+
+ g_return_if_fail (plug->priv != NULL);
+
+ g_free (plug->priv->logout_command);
+
+ remove_response_idle (plug);
+ remove_cancel_timeout (plug);
+
+ G_OBJECT_CLASS (gs_lock_plug_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gs_lock_plug_new (void)
+{
+ GtkWidget *result;
+
+ result = g_object_new (GS_TYPE_LOCK_PLUG, NULL);
+
+ gtk_window_set_focus_on_map (GTK_WINDOW (result), TRUE);
+
+ return result;
+}
diff --git a/src/gs-lock-plug.c.orig b/src/gs-lock-plug.c.orig
new file mode 100644
index 0000000..6b5856c
--- /dev/null
+++ b/src/gs-lock-plug.c.orig
@@ -0,0 +1,2050 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkx.h>
+#include <X11/XKBlib.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+#ifdef WITH_KBD_LAYOUT_INDICATOR
+#include <libmatekbd/matekbd-indicator.h>
+#endif
+
+#ifdef WITH_LIBMATENOTIFY
+#include <libmatenotify/notify.h>
+#endif
+
+#include "gs-lock-plug.h"
+
+#include "gs-debug.h"
+
+#define KEY_LOCK_DIALOG_THEME "/apps/mate-screensaver/lock_dialog_theme"
+#define MDM_FLEXISERVER_COMMAND "mdmflexiserver"
+#define MDM_FLEXISERVER_ARGS "--startnew Standard"
+
+/* same as SMS ;) */
+#define NOTE_BUFFER_MAX_CHARS 160
+
+enum
+{
+ AUTH_PAGE = 0,
+};
+
+#define FACE_ICON_SIZE 48
+#define DIALOG_TIMEOUT_MSEC 60000
+
+static void gs_lock_plug_finalize (GObject *object);
+
+#define GS_LOCK_PLUG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_LOCK_PLUG, GSLockPlugPrivate))
+
+struct GSLockPlugPrivate
+{
+ GtkWidget *vbox;
+ GtkWidget *auth_action_area;
+
+ GtkWidget *notebook;
+ GtkWidget *auth_face_image;
+ GtkWidget *auth_realname_label;
+ GtkWidget *auth_username_label;
+ GtkWidget *auth_prompt_label;
+ GtkWidget *auth_prompt_entry;
+ GtkWidget *auth_prompt_box;
+ GtkWidget *auth_capslock_label;
+ GtkWidget *auth_message_label;
+ GtkWidget *status_message_label;
+
+ GtkWidget *auth_unlock_button;
+ GtkWidget *auth_switch_button;
+ GtkWidget *auth_cancel_button;
+ GtkWidget *auth_logout_button;
+ GtkWidget *auth_note_button;
+ GtkWidget *note_tab;
+ GtkWidget *note_tab_label;
+ GtkWidget *note_text_view;
+ GtkWidget *note_ok_button;
+ GtkWidget *note_cancel_button;
+
+ GtkWidget *auth_prompt_kbd_layout_indicator;
+
+ gboolean caps_lock_on;
+ gboolean switch_enabled;
+ gboolean leave_note_enabled;
+ gboolean logout_enabled;
+ char *logout_command;
+ char *status_message;
+
+ guint timeout;
+
+ guint cancel_timeout_id;
+ guint auth_check_idle_id;
+ guint response_idle_id;
+
+ GList *key_events;
+};
+
+typedef struct _ResponseData ResponseData;
+
+struct _ResponseData
+{
+ gint response_id;
+};
+
+
+enum
+{
+ RESPONSE,
+ CLOSE,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_LOGOUT_ENABLED,
+ PROP_LOGOUT_COMMAND,
+ PROP_SWITCH_ENABLED,
+ PROP_STATUS_MESSAGE
+};
+
+static guint lock_plug_signals [LAST_SIGNAL];
+
+G_DEFINE_TYPE (GSLockPlug, gs_lock_plug, GTK_TYPE_PLUG)
+
+static void
+gs_lock_plug_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ GSLockPlug *plug;
+
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->style_set)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->style_set (widget, previous_style);
+ }
+
+ plug = GS_LOCK_PLUG (widget);
+
+ if (! plug->priv->vbox)
+ {
+ return;
+ }
+
+ gtk_container_set_border_width (GTK_CONTAINER (plug->priv->vbox), 12);
+ gtk_box_set_spacing (GTK_BOX (plug->priv->vbox), 12);
+
+ gtk_container_set_border_width (GTK_CONTAINER (plug->priv->auth_action_area), 0);
+ gtk_box_set_spacing (GTK_BOX (plug->priv->auth_action_area), 5);
+}
+
+static void
+do_user_switch (GSLockPlug *plug)
+{
+ GError *error;
+ gboolean res;
+ char *command;
+
+ command = g_strdup_printf ("%s %s",
+ MDM_FLEXISERVER_COMMAND,
+ MDM_FLEXISERVER_ARGS);
+
+ error = NULL;
+ res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (),
+ command,
+ &error);
+
+ g_free (command);
+
+ if (! res)
+ {
+ gs_debug ("Unable to start MDM greeter: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+set_status_text (GSLockPlug *plug,
+ const char *text)
+{
+ if (plug->priv->auth_message_label != NULL)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_message_label), text);
+ }
+}
+
+void
+gs_lock_plug_set_sensitive (GSLockPlug *plug,
+ gboolean sensitive)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, sensitive);
+ gtk_widget_set_sensitive (plug->priv->auth_action_area, sensitive);
+}
+
+static void
+remove_cancel_timeout (GSLockPlug *plug)
+{
+ if (plug->priv->cancel_timeout_id > 0)
+ {
+ g_source_remove (plug->priv->cancel_timeout_id);
+ plug->priv->cancel_timeout_id = 0;
+ }
+}
+
+static void
+remove_response_idle (GSLockPlug *plug)
+{
+ if (plug->priv->response_idle_id > 0)
+ {
+ g_source_remove (plug->priv->response_idle_id);
+ plug->priv->response_idle_id = 0;
+ }
+}
+
+static void
+gs_lock_plug_response (GSLockPlug *plug,
+ gint response_id)
+{
+ int new_response;
+
+ new_response = response_id;
+
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ /* Act only on response IDs we recognize */
+ if (!(response_id == GS_LOCK_PLUG_RESPONSE_OK
+ || response_id == GS_LOCK_PLUG_RESPONSE_CANCEL))
+ {
+ return;
+ }
+
+ remove_cancel_timeout (plug);
+ remove_response_idle (plug);
+
+ if (response_id == GS_LOCK_PLUG_RESPONSE_CANCEL)
+ {
+ gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), "");
+ }
+
+ g_signal_emit (plug,
+ lock_plug_signals [RESPONSE],
+ 0,
+ new_response);
+}
+
+static gboolean
+response_cancel_idle_cb (GSLockPlug *plug)
+{
+ plug->priv->response_idle_id = 0;
+
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+
+ return FALSE;
+}
+
+static gboolean
+dialog_timed_out (GSLockPlug *plug)
+{
+ gs_lock_plug_set_sensitive (plug, FALSE);
+ set_status_text (plug, _("Time has expired."));
+
+ if (plug->priv->response_idle_id != 0)
+ {
+ g_warning ("Response idle ID already set but shouldn't be");
+ }
+
+ remove_response_idle (plug);
+
+ plug->priv->response_idle_id = g_timeout_add (2000,
+ (GSourceFunc)response_cancel_idle_cb,
+ plug);
+ return FALSE;
+}
+
+
+static void
+capslock_update (GSLockPlug *plug,
+ gboolean is_on)
+{
+
+ plug->priv->caps_lock_on = is_on;
+
+ if (plug->priv->auth_capslock_label == NULL)
+ {
+ return;
+ }
+
+ if (is_on)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_capslock_label),
+ _("You have the Caps Lock key on."));
+ }
+ else
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_capslock_label),
+ "");
+ }
+}
+
+static gboolean
+is_capslock_on (void)
+{
+ XkbStateRec states;
+ Display *dsp;
+
+ dsp = GDK_DISPLAY ();
+ if (XkbGetState (dsp, XkbUseCoreKbd, &states) != Success)
+ {
+ return FALSE;
+ }
+
+ return (states.locked_mods & LockMask) != 0;
+}
+
+static void
+restart_cancel_timeout (GSLockPlug *plug)
+{
+ remove_cancel_timeout (plug);
+
+ plug->priv->cancel_timeout_id = g_timeout_add (plug->priv->timeout,
+ (GSourceFunc)dialog_timed_out,
+ plug);
+}
+
+void
+gs_lock_plug_get_text (GSLockPlug *plug,
+ char **text)
+{
+ const char *typed_text;
+ char *null_text;
+ char *local_text;
+
+ typed_text = gtk_entry_get_text (GTK_ENTRY (plug->priv->auth_prompt_entry));
+ local_text = g_locale_from_utf8 (typed_text, strlen (typed_text), NULL, NULL, NULL);
+
+ null_text = g_strnfill (strlen (typed_text) + 1, '\b');
+ gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), null_text);
+ gtk_entry_set_text (GTK_ENTRY (plug->priv->auth_prompt_entry), "");
+ g_free (null_text);
+
+ if (text != NULL)
+ {
+ *text = local_text;
+ }
+}
+
+typedef struct
+{
+ GSLockPlug *plug;
+ gint response_id;
+ GMainLoop *loop;
+ gboolean destroyed;
+} RunInfo;
+
+static void
+shutdown_loop (RunInfo *ri)
+{
+ if (g_main_loop_is_running (ri->loop))
+ g_main_loop_quit (ri->loop);
+}
+
+static void
+run_unmap_handler (GSLockPlug *plug,
+ gpointer data)
+{
+ RunInfo *ri = data;
+
+ shutdown_loop (ri);
+}
+
+static void
+run_response_handler (GSLockPlug *plug,
+ gint response_id,
+ gpointer data)
+{
+ RunInfo *ri;
+
+ ri = data;
+
+ ri->response_id = response_id;
+
+ shutdown_loop (ri);
+}
+
+static gint
+run_delete_handler (GSLockPlug *plug,
+ GdkEventAny *event,
+ gpointer data)
+{
+ RunInfo *ri = data;
+
+ shutdown_loop (ri);
+
+ return TRUE; /* Do not destroy */
+}
+
+static void
+run_destroy_handler (GSLockPlug *plug,
+ gpointer data)
+{
+ RunInfo *ri = data;
+
+ /* shutdown_loop will be called by run_unmap_handler */
+ ri->destroyed = TRUE;
+}
+
+/* adapted from GTK+ gtkdialog.c */
+int
+gs_lock_plug_run (GSLockPlug *plug)
+{
+ RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE };
+ gboolean was_modal;
+ gulong response_handler;
+ gulong unmap_handler;
+ gulong destroy_handler;
+ gulong delete_handler;
+
+ g_return_val_if_fail (GS_IS_LOCK_PLUG (plug), -1);
+
+ g_object_ref (plug);
+
+ was_modal = GTK_WINDOW (plug)->modal;
+ if (!was_modal)
+ {
+ gtk_window_set_modal (GTK_WINDOW (plug), TRUE);
+ }
+
+ if (!GTK_WIDGET_VISIBLE (plug))
+ {
+ gtk_widget_show (GTK_WIDGET (plug));
+ }
+
+ response_handler =
+ g_signal_connect (plug,
+ "response",
+ G_CALLBACK (run_response_handler),
+ &ri);
+
+ unmap_handler =
+ g_signal_connect (plug,
+ "unmap",
+ G_CALLBACK (run_unmap_handler),
+ &ri);
+
+ delete_handler =
+ g_signal_connect (plug,
+ "delete_event",
+ G_CALLBACK (run_delete_handler),
+ &ri);
+
+ destroy_handler =
+ g_signal_connect (plug,
+ "destroy",
+ G_CALLBACK (run_destroy_handler),
+ &ri);
+
+ ri.loop = g_main_loop_new (NULL, FALSE);
+
+ GDK_THREADS_LEAVE ();
+ g_main_loop_run (ri.loop);
+ GDK_THREADS_ENTER ();
+
+ g_main_loop_unref (ri.loop);
+
+ ri.loop = NULL;
+
+ if (!ri.destroyed)
+ {
+ if (! was_modal)
+ {
+ gtk_window_set_modal (GTK_WINDOW (plug), FALSE);
+ }
+
+ g_signal_handler_disconnect (plug, response_handler);
+ g_signal_handler_disconnect (plug, unmap_handler);
+ g_signal_handler_disconnect (plug, delete_handler);
+ g_signal_handler_disconnect (plug, destroy_handler);
+ }
+
+ g_object_unref (plug);
+
+ return ri.response_id;
+}
+
+
+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;
+}
+
+static void
+rounded_rectangle (cairo_t *cr,
+ gdouble aspect,
+ gdouble x,
+ gdouble y,
+ gdouble corner_radius,
+ gdouble width,
+ gdouble height)
+{
+ gdouble radius;
+ gdouble degrees;
+
+ radius = corner_radius / aspect;
+ degrees = G_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr,
+ x + width - radius,
+ y + radius,
+ radius,
+ -90 * degrees,
+ 0 * degrees);
+ cairo_arc (cr,
+ x + width - radius,
+ y + height - radius,
+ radius,
+ 0 * degrees,
+ 90 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + height - radius,
+ radius,
+ 90 * degrees,
+ 180 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + radius,
+ radius,
+ 180 * degrees,
+ 270 * degrees);
+ cairo_close_path (cr);
+}
+
+static void
+image_set_from_pixbuf (GtkImage *image,
+ GdkPixbuf *source)
+{
+ cairo_t *cr;
+ cairo_t *cr_mask;
+ cairo_surface_t *surface;
+ GdkPixmap *pixmap;
+ GdkPixmap *bitmask;
+ int w;
+ int h;
+ int frame_width;
+ double radius;
+ GdkColor color;
+ double r;
+ double g;
+ double b;
+
+ frame_width = 5;
+
+ w = gdk_pixbuf_get_width (source) + frame_width * 2;
+ h = gdk_pixbuf_get_height (source) + frame_width * 2;
+
+ radius = w / 10;
+
+ pixmap = gdk_pixmap_new (GTK_WIDGET (image)->window, w, h, -1);
+ bitmask = gdk_pixmap_new (GTK_WIDGET (image)->window, w, h, 1);
+
+ cr = gdk_cairo_create (pixmap);
+ cr_mask = gdk_cairo_create (bitmask);
+
+ /* setup mask */
+ cairo_rectangle (cr_mask, 0, 0, w, h);
+ cairo_set_operator (cr_mask, CAIRO_OPERATOR_CLEAR);
+ cairo_fill (cr_mask);
+
+ rounded_rectangle (cr_mask, 1.0, 0.5, 0.5, radius, w - 1, h - 1);
+ cairo_set_operator (cr_mask, CAIRO_OPERATOR_OVER);
+ cairo_set_source_rgb (cr_mask, 1, 1, 1);
+ cairo_fill (cr_mask);
+
+ color = GTK_WIDGET (image)->style->bg [GTK_STATE_NORMAL];
+ r = (float)color.red / 65535.0;
+ g = (float)color.green / 65535.0;
+ b = (float)color.blue / 65535.0;
+
+ /* set up image */
+ cairo_rectangle (cr, 0, 0, w, h);
+ cairo_set_source_rgb (cr, r, g, b);
+ cairo_fill (cr);
+
+ rounded_rectangle (cr,
+ 1.0,
+ frame_width + 0.5,
+ frame_width + 0.5,
+ radius,
+ w - frame_width * 2 - 1,
+ h - frame_width * 2 - 1);
+ 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);
+
+ gtk_image_set_from_pixmap (image, pixmap, bitmask);
+
+ cairo_surface_destroy (surface);
+
+ g_object_unref (bitmask);
+ g_object_unref (pixmap);
+
+ cairo_destroy (cr_mask);
+ cairo_destroy (cr);
+}
+
+
+static gboolean
+check_user_file (const gchar *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 (g_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 gboolean
+set_face_image (GSLockPlug *plug)
+{
+ GdkPixbuf *pixbuf;
+ const char *homedir;
+ char *path;
+ int icon_size = 96;
+ gsize user_max_file = 65536;
+ uid_t uid;
+
+ homedir = g_get_home_dir ();
+ uid = getuid ();
+
+ path = g_build_filename (homedir, ".face", NULL);
+
+ pixbuf = NULL;
+ if (check_user_file (path, uid, user_max_file, 0, 0))
+ {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (path,
+ icon_size,
+ icon_size,
+ NULL);
+ }
+
+ g_free (path);
+
+ if (pixbuf == NULL)
+ {
+ return FALSE;
+ }
+
+ image_set_from_pixbuf (GTK_IMAGE (plug->priv->auth_face_image), pixbuf);
+
+ g_object_unref (pixbuf);
+
+ return TRUE;
+}
+
+static void
+gs_lock_plug_show (GtkWidget *widget)
+{
+ GSLockPlug *plug = GS_LOCK_PLUG (widget);
+
+ gs_profile_start (NULL);
+
+ gs_profile_start ("parent");
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->show)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->show (widget);
+ }
+
+ gs_profile_end ("parent");
+
+
+ if (plug->priv->auth_face_image)
+ {
+ set_face_image (plug);
+ }
+
+ capslock_update (plug, is_capslock_on ());
+
+ restart_cancel_timeout (plug);
+
+ gs_profile_end (NULL);
+}
+
+static void
+gs_lock_plug_hide (GtkWidget *widget)
+{
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->hide)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->hide (widget);
+ }
+}
+
+static void
+queue_key_event (GSLockPlug *plug,
+ GdkEventKey *event)
+{
+ GdkEvent *saved_event;
+
+ saved_event = gdk_event_copy ((GdkEvent *)event);
+ plug->priv->key_events = g_list_prepend (plug->priv->key_events,
+ saved_event);
+}
+
+static void
+forward_key_events (GSLockPlug *plug)
+{
+ plug->priv->key_events = g_list_reverse (plug->priv->key_events);
+ while (plug->priv->key_events != NULL)
+ {
+ GdkEventKey *event = plug->priv->key_events->data;
+
+ gtk_window_propagate_key_event (GTK_WINDOW (plug), event);
+
+ gdk_event_free ((GdkEvent *)event);
+
+ plug->priv->key_events = g_list_delete_link (plug->priv->key_events,
+ plug->priv->key_events);
+ }
+}
+
+static void
+gs_lock_plug_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GSLockPlug *plug = GS_LOCK_PLUG (widget);
+ int mod_width;
+ int mod_height;
+
+ if (GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->size_request)
+ {
+ GTK_WIDGET_CLASS (gs_lock_plug_parent_class)->size_request (widget, requisition);
+ }
+
+ /* don't constrain size when themed */
+ if (plug->priv->vbox == NULL)
+ {
+ return;
+ }
+
+ mod_width = requisition->height * 1.3;
+ mod_height = requisition->width / 1.6;
+ if (requisition->width < mod_width)
+ {
+ /* if the dialog is tall fill out the width */
+ requisition->width = mod_width;
+ }
+ else if (requisition->height < mod_height)
+ {
+ /* if the dialog is wide fill out the height */
+ requisition->height = mod_height;
+ }
+}
+
+static void
+gs_lock_plug_set_logout_enabled (GSLockPlug *plug,
+ gboolean logout_enabled)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ if (plug->priv->logout_enabled == logout_enabled)
+ {
+ return;
+ }
+
+ plug->priv->logout_enabled = logout_enabled;
+ g_object_notify (G_OBJECT (plug), "logout-enabled");
+
+ if (plug->priv->auth_logout_button == NULL)
+ {
+ return;
+ }
+
+ if (logout_enabled)
+ {
+ gtk_widget_show (plug->priv->auth_logout_button);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_logout_button);
+ }
+}
+
+static void
+gs_lock_plug_set_logout_command (GSLockPlug *plug,
+ const char *command)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ g_free (plug->priv->logout_command);
+
+ if (command)
+ {
+ plug->priv->logout_command = g_strdup (command);
+ }
+ else
+ {
+ plug->priv->logout_command = NULL;
+ }
+}
+
+static gboolean
+is_program_in_path (const char *program)
+{
+ char *tmp = g_find_program_in_path (program);
+ if (tmp != NULL)
+ {
+ g_free (tmp);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static void
+gs_lock_plug_set_status_message (GSLockPlug *plug,
+ const char *status_message)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ g_free (plug->priv->status_message);
+ plug->priv->status_message = g_strdup (status_message);
+
+ if (plug->priv->status_message_label)
+ {
+ if (plug->priv->status_message)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->status_message_label),
+ plug->priv->status_message);
+ gtk_widget_show (plug->priv->status_message_label);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->status_message_label);
+ }
+ }
+}
+
+static void
+gs_lock_plug_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSLockPlug *self;
+
+ self = GS_LOCK_PLUG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOGOUT_ENABLED:
+ g_value_set_boolean (value, self->priv->logout_enabled);
+ break;
+ case PROP_LOGOUT_COMMAND:
+ g_value_set_string (value, self->priv->logout_command);
+ break;
+ case PROP_SWITCH_ENABLED:
+ g_value_set_boolean (value, self->priv->switch_enabled);
+ break;
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_lock_plug_set_switch_enabled (GSLockPlug *plug,
+ gboolean switch_enabled)
+{
+ g_return_if_fail (GS_LOCK_PLUG (plug));
+
+ if (plug->priv->switch_enabled == switch_enabled)
+ {
+ return;
+ }
+
+ plug->priv->switch_enabled = switch_enabled;
+ g_object_notify (G_OBJECT (plug), "switch-enabled");
+
+ if (plug->priv->auth_switch_button == NULL)
+ {
+ return;
+ }
+
+ if (switch_enabled)
+ {
+ gboolean found;
+ found = is_program_in_path (MDM_FLEXISERVER_COMMAND);
+ if (found)
+ {
+ gtk_widget_show (plug->priv->auth_switch_button);
+ }
+ else
+ {
+ gs_debug ("Waring: MDM flexiserver command not found: %s", MDM_FLEXISERVER_COMMAND);
+ gtk_widget_hide (plug->priv->auth_switch_button);
+ }
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_switch_button);
+ }
+}
+
+static void
+gs_lock_plug_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSLockPlug *self;
+
+ self = GS_LOCK_PLUG (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOGOUT_ENABLED:
+ gs_lock_plug_set_logout_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOGOUT_COMMAND:
+ gs_lock_plug_set_logout_command (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS_MESSAGE:
+ gs_lock_plug_set_status_message (self, g_value_get_string (value));
+ break;
+ case PROP_SWITCH_ENABLED:
+ gs_lock_plug_set_switch_enabled (self, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_lock_plug_close (GSLockPlug *plug)
+{
+ /* Synthesize delete_event to close dialog. */
+
+ GtkWidget *widget = GTK_WIDGET (plug);
+ GdkEvent *event;
+
+ event = gdk_event_new (GDK_DELETE);
+ event->any.window = g_object_ref (widget->window);
+ event->any.send_event = TRUE;
+
+ gtk_main_do_event (event);
+ gdk_event_free (event);
+}
+
+static void
+gs_lock_plug_class_init (GSLockPlugClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkBindingSet *binding_set;
+
+ object_class->finalize = gs_lock_plug_finalize;
+ object_class->get_property = gs_lock_plug_get_property;
+ object_class->set_property = gs_lock_plug_set_property;
+
+ widget_class->style_set = gs_lock_plug_style_set;
+ widget_class->show = gs_lock_plug_show;
+ widget_class->hide = gs_lock_plug_hide;
+ widget_class->size_request = gs_lock_plug_size_request;
+
+ klass->close = gs_lock_plug_close;
+
+ g_type_class_add_private (klass, sizeof (GSLockPlugPrivate));
+
+ lock_plug_signals [RESPONSE] = g_signal_new ("response",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSLockPlugClass, response),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1,
+ G_TYPE_INT);
+ lock_plug_signals [CLOSE] = g_signal_new ("close",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (GSLockPlugClass, close),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_ENABLED,
+ g_param_spec_boolean ("logout-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_COMMAND,
+ g_param_spec_string ("logout-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_STATUS_MESSAGE,
+ g_param_spec_string ("status-message",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_SWITCH_ENABLED,
+ g_param_spec_boolean ("switch-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ binding_set = gtk_binding_set_by_class (klass);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
+ "close", 0);
+}
+
+static void
+clear_clipboards (GSLockPlug *plug)
+{
+ GtkClipboard *clipboard;
+
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (plug), GDK_SELECTION_PRIMARY);
+ gtk_clipboard_clear (clipboard);
+ gtk_clipboard_set_text (clipboard, "", -1);
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (plug), GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_clear (clipboard);
+ gtk_clipboard_set_text (clipboard, "", -1);
+}
+
+static void
+take_note (GtkButton *button,
+ GSLockPlug *plug)
+{
+ int page;
+
+ page = gtk_notebook_page_num (GTK_NOTEBOOK (plug->priv->notebook), plug->priv->note_tab);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), page);
+ /* this counts as activity so restart the timer */
+ restart_cancel_timeout (plug);
+}
+
+static void
+submit_note (GtkButton *button,
+ GSLockPlug *plug)
+{
+#ifdef WITH_LIBMATENOTIFY
+ char *text;
+ char summary[128];
+ char *escaped_text;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
+ time_t t;
+ struct tm *tmp;
+ NotifyNotification *note;
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), AUTH_PAGE);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view));
+ gtk_text_buffer_get_bounds (buffer, &start, &end);
+ text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
+ gtk_text_buffer_set_text (buffer, "", 0);
+ escaped_text = g_markup_escape_text (text, -1);
+
+ t = time (NULL);
+ tmp = localtime (&t);
+ strftime (summary, 128, "%X", tmp);
+
+ notify_init ("mate-screensaver-dialog");
+ note = notify_notification_new (summary, escaped_text, NULL, NULL);
+ notify_notification_set_timeout (note, NOTIFY_EXPIRES_NEVER);
+ notify_notification_show (note, NULL);
+ g_object_unref (note);
+
+ g_free (text);
+ g_free (escaped_text);
+
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+#endif /* WITH_LIBMATENOTIFY */
+}
+
+static void
+cancel_note (GtkButton *button,
+ GSLockPlug *plug)
+{
+ GtkTextBuffer *buffer;
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (plug->priv->notebook), AUTH_PAGE);
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view));
+ gtk_text_buffer_set_text (buffer, "", 0);
+
+ /* this counts as activity so restart the timer */
+ restart_cancel_timeout (plug);
+
+ gtk_window_set_default (GTK_WINDOW (plug), plug->priv->auth_unlock_button);
+
+ clear_clipboards (plug);
+}
+
+static void
+logout_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+ char **argv = NULL;
+ GError *error = NULL;
+ gboolean res;
+
+ if (! plug->priv->logout_command)
+ {
+ return;
+ }
+
+ res = g_shell_parse_argv (plug->priv->logout_command, NULL, &argv, &error);
+
+ if (! res)
+ {
+ g_warning ("Could not parse logout command: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ g_spawn_async (g_get_home_dir (),
+ argv,
+ NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ NULL,
+ &error);
+
+ g_strfreev (argv);
+
+ if (error)
+ {
+ g_warning ("Could not run logout command: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+void
+gs_lock_plug_set_busy (GSLockPlug *plug)
+{
+ GdkCursor *cursor;
+ GtkWidget *top_level;
+
+ top_level = gtk_widget_get_toplevel (GTK_WIDGET (plug));
+
+ cursor = gdk_cursor_new (GDK_WATCH);
+ gdk_window_set_cursor (top_level->window, cursor);
+ gdk_cursor_unref (cursor);
+}
+
+void
+gs_lock_plug_set_ready (GSLockPlug *plug)
+{
+ GdkCursor *cursor;
+ GtkWidget *top_level;
+
+ top_level = gtk_widget_get_toplevel (GTK_WIDGET (plug));
+
+ cursor = gdk_cursor_new (GDK_LEFT_PTR);
+ gdk_window_set_cursor (top_level->window, cursor);
+ gdk_cursor_unref (cursor);
+}
+
+void
+gs_lock_plug_enable_prompt (GSLockPlug *plug,
+ const char *message,
+ gboolean visible)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ gs_debug ("Setting prompt to: %s", message);
+
+ gtk_widget_set_sensitive (plug->priv->auth_unlock_button, TRUE);
+ gtk_widget_show (plug->priv->auth_unlock_button);
+ gtk_widget_grab_default (plug->priv->auth_unlock_button);
+ gtk_label_set_text (GTK_LABEL (plug->priv->auth_prompt_label), message);
+ gtk_widget_show (plug->priv->auth_prompt_label);
+ gtk_entry_set_visibility (GTK_ENTRY (plug->priv->auth_prompt_entry), visible);
+ gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, TRUE);
+ gtk_widget_show (plug->priv->auth_prompt_entry);
+
+ if (! GTK_WIDGET_HAS_FOCUS (plug->priv->auth_prompt_entry))
+ {
+ gtk_widget_grab_focus (plug->priv->auth_prompt_entry);
+ }
+
+ /* were there any key events sent to the plug while the
+ * entry wasnt ready? If so, forward them along
+ */
+ forward_key_events (plug);
+
+ restart_cancel_timeout (plug);
+}
+
+void
+gs_lock_plug_disable_prompt (GSLockPlug *plug)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ /* gtk_widget_hide (plug->priv->auth_prompt_entry); */
+ /* gtk_widget_hide (plug->priv->auth_prompt_label); */
+ gtk_widget_set_sensitive (plug->priv->auth_unlock_button, FALSE);
+ gtk_widget_set_sensitive (plug->priv->auth_prompt_entry, FALSE);
+ /* gtk_widget_hide (plug->priv->auth_unlock_button); */
+
+ gtk_widget_grab_default (plug->priv->auth_cancel_button);
+}
+
+void
+gs_lock_plug_show_message (GSLockPlug *plug,
+ const char *message)
+{
+ g_return_if_fail (GS_IS_LOCK_PLUG (plug));
+
+ set_status_text (plug, message ? message : "");
+}
+
+/* button press handler used to inhibit popup menu */
+static gint
+entry_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+entry_key_press (GtkWidget *widget,
+ GdkEventKey *event,
+ GSLockPlug *plug)
+{
+ gboolean capslock_on;
+
+ restart_cancel_timeout (plug);
+
+ capslock_on = is_capslock_on ();
+
+ if (capslock_on != plug->priv->caps_lock_on)
+ {
+ capslock_update (plug, capslock_on);
+ }
+
+ /* if the input widget is visible and ready for input
+ * then just carry on as usual
+ */
+ if (GTK_WIDGET_VISIBLE (plug->priv->auth_prompt_entry) &&
+ GTK_WIDGET_IS_SENSITIVE (plug->priv->auth_prompt_entry))
+ {
+ return FALSE;
+ }
+
+ if (strcmp (event->string, "") == 0)
+ {
+ return FALSE;
+ }
+
+ queue_key_event (plug, event);
+
+ return TRUE;
+}
+
+/* adapted from gtk_dialog_add_button */
+static GtkWidget *
+gs_lock_plug_add_button (GSLockPlug *plug,
+ GtkWidget *action_area,
+ const gchar *button_text)
+{
+ GtkWidget *button;
+
+ g_return_val_if_fail (GS_IS_LOCK_PLUG (plug), NULL);
+ g_return_val_if_fail (button_text != NULL, NULL);
+
+ button = gtk_button_new_from_stock (button_text);
+
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+
+ gtk_widget_show (button);
+
+ gtk_box_pack_end (GTK_BOX (action_area),
+ button,
+ FALSE, TRUE, 0);
+
+ return button;
+}
+
+static char *
+get_user_display_name (void)
+{
+ const char *name;
+ char *utf8_name;
+
+ name = g_get_real_name ();
+
+ if (name == NULL || strcmp (name, "Unknown") == 0)
+ {
+ name = g_get_user_name ();
+ }
+
+ utf8_name = NULL;
+
+ if (name != NULL)
+ {
+ utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
+ }
+
+ return utf8_name;
+}
+
+static char *
+get_user_name (void)
+{
+ const char *name;
+ char *utf8_name;
+
+ name = g_get_user_name ();
+
+ utf8_name = NULL;
+ if (name != NULL)
+ {
+ utf8_name = g_locale_to_utf8 (name, -1, NULL, NULL, NULL);
+ }
+
+ return utf8_name;
+}
+
+static void
+create_page_one_buttons (GSLockPlug *plug)
+{
+
+ gs_profile_start ("page one buttons");
+
+ plug->priv->auth_switch_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ _("S_witch User..."));
+ gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (plug->priv->auth_action_area),
+ plug->priv->auth_switch_button,
+ TRUE);
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_switch_button), FALSE);
+ gtk_widget_set_no_show_all (plug->priv->auth_switch_button, TRUE);
+
+ plug->priv->auth_logout_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ _("Log _Out"));
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_logout_button), FALSE);
+ gtk_widget_set_no_show_all (plug->priv->auth_logout_button, TRUE);
+
+ plug->priv->auth_cancel_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ GTK_STOCK_CANCEL);
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_cancel_button), FALSE);
+
+ plug->priv->auth_unlock_button = gs_lock_plug_add_button (GS_LOCK_PLUG (plug),
+ plug->priv->auth_action_area,
+ _("_Unlock"));
+ gtk_button_set_focus_on_click (GTK_BUTTON (plug->priv->auth_unlock_button), FALSE);
+
+ gtk_window_set_default (GTK_WINDOW (plug), plug->priv->auth_unlock_button);
+
+ gs_profile_end ("page one buttons");
+}
+
+/* adapted from MDM */
+static char *
+expand_string (const char *text)
+{
+ GString *str;
+ const char *p;
+ char *username;
+ int i;
+ int n_chars;
+ struct utsname name;
+
+ str = g_string_sized_new (strlen (text));
+
+ p = text;
+ n_chars = g_utf8_strlen (text, -1);
+ i = 0;
+
+ while (i < n_chars)
+ {
+ gunichar ch;
+
+ ch = g_utf8_get_char (p);
+
+ /* Backslash commands */
+ if (ch == '\\')
+ {
+ p = g_utf8_next_char (p);
+ i++;
+ ch = g_utf8_get_char (p);
+
+ if (i >= n_chars || ch == '\0')
+ {
+ g_warning ("Unescaped \\ at end of text\n");
+ goto bail;
+ }
+ else if (ch == 'n')
+ {
+ g_string_append_unichar (str, '\n');
+ }
+ else
+ {
+ g_string_append_unichar (str, ch);
+ }
+ }
+ else if (ch == '%')
+ {
+ p = g_utf8_next_char (p);
+ i++;
+ ch = g_utf8_get_char (p);
+
+ if (i >= n_chars || ch == '\0')
+ {
+ g_warning ("Unescaped %% at end of text\n");
+ goto bail;
+ }
+
+ switch (ch)
+ {
+ case '%':
+ g_string_append (str, "%");
+ break;
+ case 'c':
+ /* clock */
+ break;
+ case 'd':
+ /* display */
+ g_string_append (str, g_getenv ("DISPLAY"));
+ break;
+ case 'h':
+ /* hostname */
+ g_string_append (str, g_get_host_name ());
+ break;
+ case 'm':
+ /* machine name */
+ uname (&name);
+ g_string_append (str, name.machine);
+ break;
+ case 'n':
+ /* nodename */
+ uname (&name);
+ g_string_append (str, name.nodename);
+ break;
+ case 'r':
+ /* release */
+ uname (&name);
+ g_string_append (str, name.release);
+ break;
+ case 'R':
+ /* Real name */
+ username = get_user_display_name ();
+ g_string_append (str, username);
+ g_free (username);
+ break;
+ case 's':
+ /* system name */
+ uname (&name);
+ g_string_append (str, name.sysname);
+ break;
+ case 'U':
+ /* Username */
+ username = get_user_name ();
+ g_string_append (str, username);
+ g_free (username);
+ break;
+ default:
+ if (ch < 127)
+ {
+ g_warning ("unknown escape code %%%c in text\n", (char)ch);
+ }
+ else
+ {
+ g_warning ("unknown escape code %%(U%x) in text\n", (int)ch);
+ }
+ }
+ }
+ else
+ {
+ g_string_append_unichar (str, ch);
+ }
+ p = g_utf8_next_char (p);
+ i++;
+ }
+
+bail:
+
+ return g_string_free (str, FALSE);
+}
+
+static void
+expand_string_for_label (GtkWidget *label)
+{
+ const char *template;
+ char *str;
+
+ template = gtk_label_get_label (GTK_LABEL (label));
+ str = expand_string (template);
+ gtk_label_set_label (GTK_LABEL (label), str);
+ g_free (str);
+}
+
+static void
+create_page_one (GSLockPlug *plug)
+{
+ GtkWidget *align;
+ GtkWidget *vbox;
+ GtkWidget *vbox2;
+ GtkWidget *hbox;
+ char *str;
+
+ gs_profile_start ("page one");
+
+ align = gtk_alignment_new (0.5, 0.5, 1, 1);
+ gtk_notebook_append_page (GTK_NOTEBOOK (plug->priv->notebook), align, NULL);
+
+ vbox = gtk_vbox_new (FALSE, 12);
+ gtk_container_add (GTK_CONTAINER (align), vbox);
+
+ plug->priv->auth_face_image = gtk_image_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), plug->priv->auth_face_image, TRUE, TRUE, 0);
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_face_image), 0.5, 1.0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0);
+
+ str = g_strdup ("<span size=\"x-large\">%R</span>");
+ plug->priv->auth_realname_label = gtk_label_new (str);
+ g_free (str);
+ expand_string_for_label (plug->priv->auth_realname_label);
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_realname_label), 0.5, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_realname_label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_realname_label, FALSE, FALSE, 0);
+
+ /* To translators: This expands to USERNAME on HOSTNAME */
+ str = g_strdup_printf ("<span size=\"small\">%s</span>", _("%U on %h"));
+ plug->priv->auth_username_label = gtk_label_new (str);
+ g_free (str);
+ expand_string_for_label (plug->priv->auth_username_label);
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_username_label), 0.5, 0.5);
+ gtk_label_set_use_markup (GTK_LABEL (plug->priv->auth_username_label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_username_label, FALSE, FALSE, 0);
+
+ vbox2 = gtk_vbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), vbox2, TRUE, TRUE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, FALSE, 0);
+
+ plug->priv->auth_prompt_label = gtk_label_new_with_mnemonic (_("_Password:"));
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_prompt_label), 0, 0.5);
+ gtk_box_pack_start (GTK_BOX (hbox), plug->priv->auth_prompt_label, FALSE, FALSE, 0);
+
+ plug->priv->auth_prompt_entry = gtk_entry_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), plug->priv->auth_prompt_entry, TRUE, TRUE, 0);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (plug->priv->auth_prompt_label),
+ plug->priv->auth_prompt_entry);
+
+ plug->priv->auth_capslock_label = gtk_label_new ("");
+ gtk_misc_set_alignment (GTK_MISC (plug->priv->auth_capslock_label), 0.5, 0.5);
+ gtk_box_pack_start (GTK_BOX (vbox2), plug->priv->auth_capslock_label, FALSE, FALSE, 0);
+
+ /* Status text */
+
+ plug->priv->auth_message_label = gtk_label_new (NULL);
+ gtk_box_pack_start (GTK_BOX (vbox), plug->priv->auth_message_label,
+ FALSE, FALSE, 0);
+ /* Buttons */
+ plug->priv->auth_action_area = gtk_hbutton_box_new ();
+
+ gtk_button_box_set_layout (GTK_BUTTON_BOX (plug->priv->auth_action_area),
+ GTK_BUTTONBOX_END);
+
+ gtk_box_pack_end (GTK_BOX (vbox), plug->priv->auth_action_area,
+ FALSE, TRUE, 0);
+ gtk_widget_show (plug->priv->auth_action_area);
+
+ create_page_one_buttons (plug);
+
+ gs_profile_end ("page one");
+}
+
+static void
+unlock_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_OK);
+}
+
+static void
+cancel_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+}
+
+static void
+switch_user_button_clicked (GtkButton *button,
+ GSLockPlug *plug)
+{
+
+ remove_response_idle (plug);
+
+ gs_lock_plug_set_sensitive (plug, FALSE);
+
+ plug->priv->response_idle_id = g_timeout_add (2000,
+ (GSourceFunc)response_cancel_idle_cb,
+ plug);
+
+ gs_lock_plug_set_busy (plug);
+ do_user_switch (plug);
+}
+
+static char *
+get_dialog_theme_name (GSLockPlug *plug)
+{
+ char *name;
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+ name = mateconf_client_get_string (client, KEY_LOCK_DIALOG_THEME, NULL);
+ g_object_unref (client);
+
+ return name;
+}
+
+static gboolean
+load_theme (GSLockPlug *plug)
+{
+ char *theme;
+ char *filename;
+ char *gtkbuilder;
+ char *rc;
+ GtkBuilder *builder;
+ GtkWidget *lock_dialog;
+ GError *error=NULL;
+
+ theme = get_dialog_theme_name (plug);
+ if (theme == NULL)
+ {
+ return FALSE;
+ }
+
+ filename = g_strdup_printf ("lock-dialog-%s.ui", theme);
+ gtkbuilder = g_build_filename (GTKBUILDERDIR, filename, NULL);
+ g_free (filename);
+ if (! g_file_test (gtkbuilder, G_FILE_TEST_IS_REGULAR))
+ {
+ g_free (gtkbuilder);
+ g_free (theme);
+ return FALSE;
+ }
+
+ filename = g_strdup_printf ("lock-dialog-%s.gtkrc", theme);
+ g_free (theme);
+
+ rc = g_build_filename (GTKBUILDERDIR, filename, NULL);
+ g_free (filename);
+ if (g_file_test (rc, G_FILE_TEST_IS_REGULAR))
+ {
+ gtk_rc_parse (rc);
+ }
+ g_free (rc);
+
+ builder = gtk_builder_new();
+
+ if (!gtk_builder_add_from_file (builder,gtkbuilder,&error))
+ {
+ g_warning ("Couldn't load builder file '%s': %s", gtkbuilder, error->message);
+ g_error_free(error);
+ g_free (gtkbuilder);
+ return FALSE;
+ }
+ g_free (gtkbuilder);
+
+ lock_dialog = GTK_WIDGET (gtk_builder_get_object(builder, "lock-dialog"));
+ gtk_container_add (GTK_CONTAINER (plug), lock_dialog);
+
+ plug->priv->vbox = NULL;
+ plug->priv->notebook = GTK_WIDGET (gtk_builder_get_object(builder, "notebook"));
+
+ plug->priv->auth_face_image = GTK_WIDGET (gtk_builder_get_object(builder, "auth-face-image"));
+ plug->priv->auth_action_area = GTK_WIDGET (gtk_builder_get_object(builder, "auth-action-area"));
+ plug->priv->auth_realname_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-realname-label"));
+ plug->priv->auth_username_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-username-label"));
+ plug->priv->auth_prompt_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-label"));
+ plug->priv->auth_prompt_entry = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-entry"));
+ plug->priv->auth_prompt_box = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-box"));
+ plug->priv->auth_capslock_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-capslock-label"));
+ plug->priv->auth_message_label = GTK_WIDGET (gtk_builder_get_object(builder, "auth-status-label"));
+ plug->priv->auth_unlock_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-unlock-button"));
+ plug->priv->auth_cancel_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-cancel-button"));
+ plug->priv->auth_logout_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-logout-button"));
+ plug->priv->auth_switch_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-switch-button"));
+ plug->priv->auth_note_button = GTK_WIDGET (gtk_builder_get_object(builder, "auth-note-button"));
+ plug->priv->note_tab = GTK_WIDGET (gtk_builder_get_object(builder, "note-tab"));
+ plug->priv->note_tab_label = GTK_WIDGET (gtk_builder_get_object(builder, "note-tab-label"));
+ plug->priv->note_ok_button = GTK_WIDGET (gtk_builder_get_object(builder, "note-ok-button"));
+ plug->priv->note_text_view = GTK_WIDGET (gtk_builder_get_object(builder, "note-text-view"));
+ plug->priv->note_cancel_button = GTK_WIDGET (gtk_builder_get_object(builder, "note-cancel-button"));
+
+ /* Placeholder for the keyboard indicator */
+ plug->priv->auth_prompt_kbd_layout_indicator = GTK_WIDGET (gtk_builder_get_object(builder, "auth-prompt-kbd-layout-indicator"));
+ if (plug->priv->auth_logout_button != NULL)
+ {
+ gtk_widget_set_no_show_all (plug->priv->auth_logout_button, TRUE);
+ }
+ if (plug->priv->auth_switch_button != NULL)
+ {
+ gtk_widget_set_no_show_all (plug->priv->auth_switch_button, TRUE);
+ }
+ if (plug->priv->auth_note_button != NULL)
+ {
+ gtk_widget_set_no_show_all (plug->priv->auth_note_button, TRUE);
+ }
+
+ gtk_widget_show_all (lock_dialog);
+
+ plug->priv->status_message_label = GTK_WIDGET (gtk_builder_get_object(builder, "status-message-label"));
+
+ return TRUE;
+}
+
+static int
+delete_handler (GSLockPlug *plug,
+ GdkEventAny *event,
+ gpointer data)
+{
+ gs_lock_plug_response (plug, GS_LOCK_PLUG_RESPONSE_CANCEL);
+
+ return TRUE; /* Do not destroy */
+}
+
+static void
+on_note_text_buffer_changed (GtkTextBuffer *buffer,
+ GSLockPlug *plug)
+{
+ int len;
+
+ len = gtk_text_buffer_get_char_count (buffer);
+ if (len > NOTE_BUFFER_MAX_CHARS)
+ {
+ gtk_widget_set_sensitive (plug->priv->note_text_view, FALSE);
+ }
+}
+
+static void
+gs_lock_plug_init (GSLockPlug *plug)
+{
+ gs_profile_start (NULL);
+
+ plug->priv = GS_LOCK_PLUG_GET_PRIVATE (plug);
+
+ clear_clipboards (plug);
+
+#ifdef WITH_LIBMATENOTIFY
+ plug->priv->leave_note_enabled = TRUE;
+#else
+ plug->priv->leave_note_enabled = FALSE;
+#endif
+
+ if (! load_theme (plug))
+ {
+ gs_debug ("Unable to load theme!");
+
+ plug->priv->vbox = gtk_vbox_new (FALSE, 0);
+
+ gtk_container_add (GTK_CONTAINER (plug), plug->priv->vbox);
+
+ /* Notebook */
+
+ plug->priv->notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (plug->priv->notebook), FALSE);
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (plug->priv->notebook), FALSE);
+ gtk_box_pack_start (GTK_BOX (plug->priv->vbox), plug->priv->notebook, TRUE, TRUE, 0);
+
+ /* Page 1 */
+
+ create_page_one (plug);
+
+ gtk_widget_show_all (plug->priv->vbox);
+ }
+
+ if (plug->priv->note_text_view != NULL)
+ {
+ GtkTextBuffer *buffer;
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (plug->priv->note_text_view));
+ g_signal_connect (buffer, "changed", G_CALLBACK (on_note_text_buffer_changed), plug);
+ }
+
+ /* Layout indicator */
+#ifdef WITH_KBD_LAYOUT_INDICATOR
+ if (plug->priv->auth_prompt_kbd_layout_indicator != NULL)
+ {
+ XklEngine *engine;
+
+ engine = xkl_engine_get_instance (GDK_DISPLAY ());
+ if (xkl_engine_get_num_groups (engine) > 1)
+ {
+ GtkWidget *layout_indicator;
+
+ layout_indicator = matekbd_indicator_new ();
+ matekbd_indicator_set_parent_tooltips (MATEKBD_INDICATOR (layout_indicator), TRUE);
+ gtk_box_pack_start (GTK_BOX (plug->priv->auth_prompt_kbd_layout_indicator),
+ layout_indicator,
+ FALSE,
+ FALSE,
+ 6);
+
+ gtk_widget_show_all (layout_indicator);
+ gtk_widget_show (plug->priv->auth_prompt_kbd_layout_indicator);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_prompt_kbd_layout_indicator);
+ }
+
+ g_object_unref (engine);
+ }
+#endif
+
+ if (plug->priv->auth_note_button != NULL)
+ {
+ if (plug->priv->leave_note_enabled)
+ {
+ gtk_widget_show_all (plug->priv->auth_note_button);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_note_button);
+ }
+ }
+ if (plug->priv->auth_switch_button != NULL)
+ {
+ if (plug->priv->switch_enabled)
+ {
+ gtk_widget_show_all (plug->priv->auth_switch_button);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->auth_switch_button);
+ }
+ }
+
+ gtk_widget_grab_default (plug->priv->auth_unlock_button);
+
+ if (plug->priv->auth_username_label != NULL)
+ {
+ expand_string_for_label (plug->priv->auth_username_label);
+ }
+
+ if (plug->priv->auth_realname_label != NULL)
+ {
+ expand_string_for_label (plug->priv->auth_realname_label);
+ }
+
+ if (! plug->priv->logout_enabled || ! plug->priv->logout_command)
+ {
+ if (plug->priv->auth_logout_button != NULL)
+ {
+ gtk_widget_hide (plug->priv->auth_logout_button);
+ }
+ }
+
+ plug->priv->timeout = DIALOG_TIMEOUT_MSEC;
+
+ g_signal_connect (plug, "key_press_event",
+ G_CALLBACK (entry_key_press), plug);
+
+ /* button press handler used to inhibit popup menu */
+ g_signal_connect (plug->priv->auth_prompt_entry, "button_press_event",
+ G_CALLBACK (entry_button_press), NULL);
+ gtk_entry_set_activates_default (GTK_ENTRY (plug->priv->auth_prompt_entry), TRUE);
+ gtk_entry_set_visibility (GTK_ENTRY (plug->priv->auth_prompt_entry), FALSE);
+
+ g_signal_connect (plug->priv->auth_unlock_button, "clicked",
+ G_CALLBACK (unlock_button_clicked), plug);
+
+ g_signal_connect (plug->priv->auth_cancel_button, "clicked",
+ G_CALLBACK (cancel_button_clicked), plug);
+
+ if (plug->priv->status_message_label)
+ {
+ if (plug->priv->status_message)
+ {
+ gtk_label_set_text (GTK_LABEL (plug->priv->status_message_label),
+ plug->priv->status_message);
+ }
+ else
+ {
+ gtk_widget_hide (plug->priv->status_message_label);
+ }
+ }
+
+ if (plug->priv->auth_switch_button != NULL)
+ {
+ g_signal_connect (plug->priv->auth_switch_button, "clicked",
+ G_CALLBACK (switch_user_button_clicked), plug);
+ }
+
+ if (plug->priv->auth_note_button != NULL)
+ {
+ g_signal_connect (plug->priv->auth_note_button, "clicked",
+ G_CALLBACK (take_note), plug);
+ g_signal_connect (plug->priv->note_ok_button, "clicked",
+ G_CALLBACK (submit_note), plug);
+ g_signal_connect (plug->priv->note_cancel_button, "clicked",
+ G_CALLBACK (cancel_note), plug);
+ }
+
+ if (plug->priv->note_tab_label != NULL)
+ {
+ expand_string_for_label (plug->priv->note_tab_label);
+ }
+
+ if (plug->priv->auth_logout_button != NULL)
+ {
+ g_signal_connect (plug->priv->auth_logout_button, "clicked",
+ G_CALLBACK (logout_button_clicked), plug);
+ }
+
+ g_signal_connect (plug, "delete_event", G_CALLBACK (delete_handler), NULL);
+
+ gs_profile_end (NULL);
+}
+
+static void
+gs_lock_plug_finalize (GObject *object)
+{
+ GSLockPlug *plug;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_LOCK_PLUG (object));
+
+ plug = GS_LOCK_PLUG (object);
+
+ g_return_if_fail (plug->priv != NULL);
+
+ g_free (plug->priv->logout_command);
+
+ remove_response_idle (plug);
+ remove_cancel_timeout (plug);
+
+ G_OBJECT_CLASS (gs_lock_plug_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gs_lock_plug_new (void)
+{
+ GtkWidget *result;
+
+ result = g_object_new (GS_TYPE_LOCK_PLUG, NULL);
+
+ gtk_window_set_focus_on_map (GTK_WINDOW (result), TRUE);
+
+ return result;
+}
diff --git a/src/gs-lock-plug.h b/src/gs-lock-plug.h
new file mode 100644
index 0000000..3217bbf
--- /dev/null
+++ b/src/gs-lock-plug.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_LOCK_PLUG_H
+#define __GS_LOCK_PLUG_H
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GS_LOCK_PLUG_RESPONSE_NONE = -1,
+ GS_LOCK_PLUG_RESPONSE_OK = -2,
+ GS_LOCK_PLUG_RESPONSE_CANCEL = -3
+} GSPlugResponseType;
+
+#define GS_TYPE_LOCK_PLUG (gs_lock_plug_get_type ())
+#define GS_LOCK_PLUG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_LOCK_PLUG, GSLockPlug))
+#define GS_LOCK_PLUG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_LOCK_PLUG, GSLockPlugClass))
+#define GS_IS_LOCK_PLUG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_LOCK_PLUG))
+#define GS_IS_LOCK_PLUG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_LOCK_PLUG))
+#define GS_LOCK_PLUG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_LOCK_PLUG, GSLockPlugClass))
+
+typedef struct GSLockPlugPrivate GSLockPlugPrivate;
+
+typedef struct
+{
+ GtkPlug parent;
+
+ GSLockPlugPrivate *priv;
+} GSLockPlug;
+
+typedef struct
+{
+ GtkPlugClass parent_class;
+
+ void (* response) (GSLockPlug *plug, gint response_id);
+
+ /* Keybinding signals */
+ void (* close) (GSLockPlug *plug);
+
+} GSLockPlugClass;
+
+GType gs_lock_plug_get_type (void);
+GtkWidget * gs_lock_plug_new (void);
+
+int gs_lock_plug_run (GSLockPlug *plug);
+void gs_lock_plug_set_sensitive (GSLockPlug *plug,
+ gboolean sensitive);
+void gs_lock_plug_enable_prompt (GSLockPlug *plug,
+ const char *message,
+ gboolean visible);
+void gs_lock_plug_disable_prompt (GSLockPlug *plug);
+void gs_lock_plug_set_busy (GSLockPlug *plug);
+void gs_lock_plug_set_ready (GSLockPlug *plug);
+
+void gs_lock_plug_get_text (GSLockPlug *plug,
+ char **text);
+void gs_lock_plug_show_message (GSLockPlug *plug,
+ const char *message);
+
+G_END_DECLS
+
+#endif /* __GS_LOCK_PLUG_H */
diff --git a/src/gs-lock-plug.h.orig b/src/gs-lock-plug.h.orig
new file mode 100644
index 0000000..cc71f21
--- /dev/null
+++ b/src/gs-lock-plug.h.orig
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_LOCK_PLUG_H
+#define __GS_LOCK_PLUG_H
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ GS_LOCK_PLUG_RESPONSE_NONE = -1,
+ GS_LOCK_PLUG_RESPONSE_OK = -2,
+ GS_LOCK_PLUG_RESPONSE_CANCEL = -3
+} GSPlugResponseType;
+
+#define GS_TYPE_LOCK_PLUG (gs_lock_plug_get_type ())
+#define GS_LOCK_PLUG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_LOCK_PLUG, GSLockPlug))
+#define GS_LOCK_PLUG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_LOCK_PLUG, GSLockPlugClass))
+#define GS_IS_LOCK_PLUG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_LOCK_PLUG))
+#define GS_IS_LOCK_PLUG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_LOCK_PLUG))
+#define GS_LOCK_PLUG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_LOCK_PLUG, GSLockPlugClass))
+
+typedef struct GSLockPlugPrivate GSLockPlugPrivate;
+
+typedef struct
+{
+ GtkPlug parent;
+
+ GSLockPlugPrivate *priv;
+} GSLockPlug;
+
+typedef struct
+{
+ GtkPlugClass parent_class;
+
+ void (* response) (GSLockPlug *plug, gint response_id);
+
+ /* Keybinding signals */
+ void (* close) (GSLockPlug *plug);
+
+} GSLockPlugClass;
+
+GType gs_lock_plug_get_type (void);
+GtkWidget * gs_lock_plug_new (void);
+
+int gs_lock_plug_run (GSLockPlug *plug);
+void gs_lock_plug_set_sensitive (GSLockPlug *plug,
+ gboolean sensitive);
+void gs_lock_plug_enable_prompt (GSLockPlug *plug,
+ const char *message,
+ gboolean visible);
+void gs_lock_plug_disable_prompt (GSLockPlug *plug);
+void gs_lock_plug_set_busy (GSLockPlug *plug);
+void gs_lock_plug_set_ready (GSLockPlug *plug);
+
+void gs_lock_plug_get_text (GSLockPlug *plug,
+ char **text);
+void gs_lock_plug_show_message (GSLockPlug *plug,
+ const char *message);
+
+G_END_DECLS
+
+#endif /* __GS_LOCK_PLUG_H */
diff --git a/src/gs-manager.c b/src/gs-manager.c
new file mode 100644
index 0000000..3deca22
--- /dev/null
+++ b/src/gs-manager.c
@@ -0,0 +1,2030 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2008 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <time.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include <mateconf/mateconf-engine.h>
+#include <mateconf/mateconf-client.h>
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+#include <libmateui/mate-bg.h>
+
+#include "gs-prefs.h" /* for GSSaverMode */
+
+#include "gs-manager.h"
+#include "gs-window.h"
+#include "gs-theme-manager.h"
+#include "gs-job.h"
+#include "gs-grab.h"
+#include "gs-fade.h"
+#include "gs-debug.h"
+
+static void gs_manager_class_init (GSManagerClass *klass);
+static void gs_manager_init (GSManager *manager);
+static void gs_manager_finalize (GObject *object);
+
+#define GS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_MANAGER, GSManagerPrivate))
+
+struct GSManagerPrivate
+{
+ GSList *windows;
+ GHashTable *jobs;
+
+ GSThemeManager *theme_manager;
+ MateConfClient *client;
+ MateBG *bg;
+ guint bg_notify_id;
+
+ /* Policy */
+ glong lock_timeout;
+ glong cycle_timeout;
+ glong logout_timeout;
+
+ guint lock_enabled : 1;
+ guint logout_enabled : 1;
+ guint keyboard_enabled : 1;
+ guint user_switch_enabled : 1;
+ guint throttled : 1;
+
+ char *logout_command;
+ char *keyboard_command;
+
+ char *status_message;
+
+ /* State */
+ guint active : 1;
+ guint lock_active : 1;
+
+ guint fading : 1;
+ guint dialog_up : 1;
+
+ time_t activate_time;
+
+ guint lock_timeout_id;
+ guint cycle_timeout_id;
+
+ GSList *themes;
+ GSSaverMode saver_mode;
+ GSGrab *grab;
+ GSFade *fade;
+ guint unfade_idle_id;
+};
+
+enum
+{
+ ACTIVATED,
+ DEACTIVATED,
+ AUTH_REQUEST_BEGIN,
+ AUTH_REQUEST_END,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_LOCK_ENABLED,
+ PROP_LOGOUT_ENABLED,
+ PROP_USER_SWITCH_ENABLED,
+ PROP_KEYBOARD_ENABLED,
+ PROP_LOCK_TIMEOUT,
+ PROP_CYCLE_TIMEOUT,
+ PROP_LOGOUT_TIMEOUT,
+ PROP_LOGOUT_COMMAND,
+ PROP_KEYBOARD_COMMAND,
+ PROP_STATUS_MESSAGE,
+ PROP_ACTIVE,
+ PROP_THROTTLED,
+};
+
+#define FADE_TIMEOUT 1000
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSManager, gs_manager, G_TYPE_OBJECT)
+
+static void
+manager_add_job_for_window (GSManager *manager,
+ GSWindow *window,
+ GSJob *job)
+{
+ if (manager->priv->jobs == NULL)
+ {
+ return;
+ }
+
+ g_hash_table_insert (manager->priv->jobs, window, job);
+}
+
+static const char *
+select_theme (GSManager *manager)
+{
+ const char *theme = NULL;
+
+ g_return_val_if_fail (manager != NULL, NULL);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), NULL);
+
+ if (manager->priv->saver_mode == GS_MODE_BLANK_ONLY)
+ {
+ return NULL;
+ }
+
+ if (manager->priv->themes)
+ {
+ int number = 0;
+
+ if (manager->priv->saver_mode == GS_MODE_RANDOM)
+ {
+ g_random_set_seed (time (NULL));
+ number = g_random_int_range (0, g_slist_length (manager->priv->themes));
+ }
+ theme = g_slist_nth_data (manager->priv->themes, number);
+ }
+
+ return theme;
+}
+
+static GSJob *
+lookup_job_for_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ if (manager->priv->jobs == NULL)
+ {
+ return NULL;
+ }
+
+ job = g_hash_table_lookup (manager->priv->jobs, window);
+
+ return job;
+}
+
+static void
+manager_maybe_stop_job_for_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ job = lookup_job_for_window (manager, window);
+
+ if (job == NULL)
+ {
+ gs_debug ("Job not found for window");
+ return;
+ }
+
+ gs_job_stop (job);
+}
+
+static void
+manager_maybe_start_job_for_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ job = lookup_job_for_window (manager, window);
+
+ if (job == NULL)
+ {
+ gs_debug ("Job not found for window");
+ return;
+ }
+
+ if (! manager->priv->dialog_up)
+ {
+ if (! manager->priv->throttled)
+ {
+ if (! gs_job_is_running (job))
+ {
+ if (! gs_window_is_obscured (window))
+ {
+ gs_debug ("Starting job for window");
+ gs_job_start (job);
+ }
+ else
+ {
+ gs_debug ("Window is obscured deferring start of job");
+ }
+ }
+ else
+ {
+ gs_debug ("Not starting job because job is running");
+ }
+ }
+ else
+ {
+ gs_debug ("Not starting job because throttled");
+ }
+ }
+ else
+ {
+ gs_debug ("Not starting job because dialog is up");
+ }
+}
+
+static void
+manager_select_theme_for_job (GSManager *manager,
+ GSJob *job)
+{
+ const char *theme;
+
+ theme = select_theme (manager);
+
+ if (theme != NULL)
+ {
+ GSThemeInfo *info;
+ const char *command;
+
+ command = NULL;
+
+ info = gs_theme_manager_lookup_theme_info (manager->priv->theme_manager, theme);
+ if (info != NULL)
+ {
+ command = gs_theme_info_get_exec (info);
+ }
+ else
+ {
+ gs_debug ("Could not find information for theme: %s",
+ theme);
+ }
+
+ gs_job_set_command (job, command);
+
+
+ if (info != NULL)
+ {
+ gs_theme_info_unref (info);
+ }
+ }
+ else
+ {
+ gs_job_set_command (job, NULL);
+ }
+}
+
+static void
+cycle_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ gs_job_stop (job);
+ manager_select_theme_for_job (manager, job);
+ manager_maybe_start_job_for_window (manager, window);
+}
+
+static void
+manager_cycle_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) cycle_job, manager);
+ }
+}
+
+static void
+throttle_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ if (manager->priv->throttled)
+ {
+ gs_job_stop (job);
+ }
+ else
+ {
+ manager_maybe_start_job_for_window (manager, window);
+ }
+}
+
+static void
+manager_throttle_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) throttle_job, manager);
+ }
+}
+
+static void
+resume_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ if (gs_job_is_running (job))
+ {
+ gs_job_suspend (job, FALSE);
+ }
+ else
+ {
+ manager_maybe_start_job_for_window (manager, window);
+ }
+}
+
+static void
+manager_resume_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) resume_job, manager);
+ }
+}
+
+static void
+suspend_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ gs_job_suspend (job, TRUE);
+}
+
+static void
+manager_suspend_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) suspend_job, manager);
+ }
+}
+
+static void
+manager_stop_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_destroy (manager->priv->jobs);
+
+ }
+ manager->priv->jobs = NULL;
+}
+
+void
+gs_manager_set_mode (GSManager *manager,
+ GSSaverMode mode)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ manager->priv->saver_mode = mode;
+}
+
+static void
+free_themes (GSManager *manager)
+{
+ if (manager->priv->themes)
+ {
+ g_slist_foreach (manager->priv->themes, (GFunc)g_free, NULL);
+ g_slist_free (manager->priv->themes);
+ }
+}
+
+void
+gs_manager_set_themes (GSManager *manager,
+ GSList *themes)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ free_themes (manager);
+ manager->priv->themes = NULL;
+
+ for (l = themes; l; l = l->next)
+ {
+ manager->priv->themes = g_slist_append (manager->priv->themes, g_strdup (l->data));
+ }
+}
+
+void
+gs_manager_set_throttled (GSManager *manager,
+ gboolean throttled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->throttled != throttled)
+ {
+ GSList *l;
+
+ manager->priv->throttled = throttled;
+
+ if (! manager->priv->dialog_up)
+ {
+
+ manager_throttle_jobs (manager);
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_clear (l->data);
+ }
+ }
+ }
+}
+
+void
+gs_manager_get_lock_active (GSManager *manager,
+ gboolean *lock_active)
+{
+ if (lock_active != NULL)
+ {
+ *lock_active = FALSE;
+ }
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ *lock_active = manager->priv->lock_active;
+}
+
+void
+gs_manager_set_lock_active (GSManager *manager,
+ gboolean lock_active)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ gs_debug ("Setting lock active: %d", lock_active);
+
+ if (manager->priv->lock_active != lock_active)
+ {
+ GSList *l;
+
+ manager->priv->lock_active = lock_active;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_lock_enabled (l->data, lock_active);
+ }
+ }
+}
+
+void
+gs_manager_get_lock_enabled (GSManager *manager,
+ gboolean *lock_enabled)
+{
+ if (lock_enabled != NULL)
+ {
+ *lock_enabled = FALSE;
+ }
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ *lock_enabled = manager->priv->lock_enabled;
+}
+
+void
+gs_manager_set_lock_enabled (GSManager *manager,
+ gboolean lock_enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->lock_enabled != lock_enabled)
+ {
+ manager->priv->lock_enabled = lock_enabled;
+ }
+}
+
+void
+gs_manager_set_logout_enabled (GSManager *manager,
+ gboolean logout_enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->logout_enabled != logout_enabled)
+ {
+ GSList *l;
+
+ manager->priv->logout_enabled = logout_enabled;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_logout_enabled (l->data, logout_enabled);
+ }
+ }
+}
+
+void
+gs_manager_set_keyboard_enabled (GSManager *manager,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->keyboard_enabled != enabled)
+ {
+ GSList *l;
+
+ manager->priv->keyboard_enabled = enabled;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_keyboard_enabled (l->data, enabled);
+ }
+ }
+}
+
+void
+gs_manager_set_user_switch_enabled (GSManager *manager,
+ gboolean user_switch_enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->user_switch_enabled != user_switch_enabled)
+ {
+ GSList *l;
+
+ manager->priv->user_switch_enabled = user_switch_enabled;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_user_switch_enabled (l->data, user_switch_enabled);
+ }
+ }
+}
+
+static gboolean
+activate_lock_timeout (GSManager *manager)
+{
+ if (manager->priv->lock_enabled)
+ {
+ gs_manager_set_lock_active (manager, TRUE);
+ }
+
+ manager->priv->lock_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+remove_lock_timer (GSManager *manager)
+{
+ if (manager->priv->lock_timeout_id != 0)
+ {
+ g_source_remove (manager->priv->lock_timeout_id);
+ manager->priv->lock_timeout_id = 0;
+ }
+}
+
+static void
+add_lock_timer (GSManager *manager,
+ glong timeout)
+{
+ manager->priv->lock_timeout_id = g_timeout_add (timeout,
+ (GSourceFunc)activate_lock_timeout,
+ manager);
+}
+
+void
+gs_manager_set_lock_timeout (GSManager *manager,
+ glong lock_timeout)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->lock_timeout != lock_timeout)
+ {
+
+ manager->priv->lock_timeout = lock_timeout;
+
+ if (manager->priv->active
+ && ! manager->priv->lock_active
+ && (lock_timeout >= 0))
+ {
+
+ glong elapsed = (time (NULL) - manager->priv->activate_time) * 1000;
+
+ remove_lock_timer (manager);
+
+ if (elapsed >= lock_timeout)
+ {
+ activate_lock_timeout (manager);
+ }
+ else
+ {
+ add_lock_timer (manager, lock_timeout - elapsed);
+ }
+ }
+ }
+}
+
+void
+gs_manager_set_logout_timeout (GSManager *manager,
+ glong logout_timeout)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->logout_timeout != logout_timeout)
+ {
+ GSList *l;
+
+ manager->priv->logout_timeout = logout_timeout;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_logout_timeout (l->data, logout_timeout);
+ }
+ }
+}
+
+void
+gs_manager_set_logout_command (GSManager *manager,
+ const char *command)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_free (manager->priv->logout_command);
+
+ if (command)
+ {
+ manager->priv->logout_command = g_strdup (command);
+ }
+ else
+ {
+ manager->priv->logout_command = NULL;
+ }
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_logout_command (l->data, manager->priv->logout_command);
+ }
+}
+
+void
+gs_manager_set_keyboard_command (GSManager *manager,
+ const char *command)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_free (manager->priv->keyboard_command);
+
+ if (command)
+ {
+ manager->priv->keyboard_command = g_strdup (command);
+ }
+ else
+ {
+ manager->priv->keyboard_command = NULL;
+ }
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_keyboard_command (l->data, manager->priv->keyboard_command);
+ }
+}
+
+void
+gs_manager_set_status_message (GSManager *manager,
+ const char *status_message)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_free (manager->priv->status_message);
+
+ manager->priv->status_message = g_strdup (status_message);
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_status_message (l->data, manager->priv->status_message);
+ }
+}
+
+gboolean
+gs_manager_cycle (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ gs_debug ("cycling jobs");
+
+ if (! manager->priv->active)
+ {
+ return FALSE;
+ }
+
+ if (manager->priv->dialog_up)
+ {
+ return FALSE;
+ }
+
+ if (manager->priv->throttled)
+ {
+ return FALSE;
+ }
+
+ manager_cycle_jobs (manager);
+
+ return TRUE;
+}
+
+static gboolean
+cycle_timeout (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (! manager->priv->dialog_up)
+ {
+ gs_manager_cycle (manager);
+ }
+
+ return TRUE;
+}
+
+static void
+remove_cycle_timer (GSManager *manager)
+{
+ if (manager->priv->cycle_timeout_id != 0)
+ {
+ g_source_remove (manager->priv->cycle_timeout_id);
+ manager->priv->cycle_timeout_id = 0;
+ }
+}
+
+static void
+add_cycle_timer (GSManager *manager,
+ glong timeout)
+{
+ manager->priv->cycle_timeout_id = g_timeout_add (timeout,
+ (GSourceFunc)cycle_timeout,
+ manager);
+}
+
+void
+gs_manager_set_cycle_timeout (GSManager *manager,
+ glong cycle_timeout)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->cycle_timeout != cycle_timeout)
+ {
+
+ manager->priv->cycle_timeout = cycle_timeout;
+
+ if (manager->priv->active && (cycle_timeout >= 0))
+ {
+ glong timeout;
+ glong elapsed = (time (NULL) - manager->priv->activate_time) * 1000;
+
+ remove_cycle_timer (manager);
+
+ if (elapsed >= cycle_timeout)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = cycle_timeout - elapsed;
+ }
+
+ add_cycle_timer (manager, timeout);
+
+ }
+ }
+}
+
+static void
+gs_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSManager *self;
+
+ self = GS_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_THROTTLED:
+ gs_manager_set_throttled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOCK_ENABLED:
+ gs_manager_set_lock_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOCK_TIMEOUT:
+ gs_manager_set_lock_timeout (self, g_value_get_long (value));
+ break;
+ case PROP_LOGOUT_ENABLED:
+ gs_manager_set_logout_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ gs_manager_set_keyboard_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_USER_SWITCH_ENABLED:
+ gs_manager_set_user_switch_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ gs_manager_set_logout_timeout (self, g_value_get_long (value));
+ break;
+ case PROP_LOGOUT_COMMAND:
+ gs_manager_set_logout_command (self, g_value_get_string (value));
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ gs_manager_set_keyboard_command (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS_MESSAGE:
+ gs_manager_set_status_message (self, g_value_get_string (value));
+ break;
+ case PROP_CYCLE_TIMEOUT:
+ gs_manager_set_cycle_timeout (self, g_value_get_long (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSManager *self;
+
+ self = GS_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_THROTTLED:
+ g_value_set_boolean (value, self->priv->throttled);
+ break;
+ case PROP_LOCK_ENABLED:
+ g_value_set_boolean (value, self->priv->lock_enabled);
+ break;
+ case PROP_LOCK_TIMEOUT:
+ g_value_set_long (value, self->priv->lock_timeout);
+ break;
+ case PROP_LOGOUT_ENABLED:
+ g_value_set_boolean (value, self->priv->logout_enabled);
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ g_value_set_boolean (value, self->priv->keyboard_enabled);
+ break;
+ case PROP_USER_SWITCH_ENABLED:
+ g_value_set_boolean (value, self->priv->user_switch_enabled);
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ g_value_set_long (value, self->priv->logout_timeout);
+ break;
+ case PROP_LOGOUT_COMMAND:
+ g_value_set_string (value, self->priv->logout_command);
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ g_value_set_string (value, self->priv->keyboard_command);
+ break;
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ case PROP_CYCLE_TIMEOUT:
+ g_value_set_long (value, self->priv->cycle_timeout);
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, self->priv->active);
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_manager_class_init (GSManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_manager_finalize;
+ object_class->get_property = gs_manager_get_property;
+ object_class->set_property = gs_manager_set_property;
+
+ signals [ACTIVATED] =
+ g_signal_new ("activated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, activated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [DEACTIVATED] =
+ g_signal_new ("deactivated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, deactivated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [AUTH_REQUEST_BEGIN] =
+ g_signal_new ("auth-request-begin",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, auth_request_begin),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [AUTH_REQUEST_END] =
+ g_signal_new ("auth-request-end",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, auth_request_end),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_LOCK_ENABLED,
+ g_param_spec_boolean ("lock-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOCK_TIMEOUT,
+ g_param_spec_long ("lock-timeout",
+ NULL,
+ NULL,
+ -1,
+ G_MAXLONG,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_ENABLED,
+ g_param_spec_boolean ("logout-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_USER_SWITCH_ENABLED,
+ g_param_spec_boolean ("user-switch-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_TIMEOUT,
+ g_param_spec_long ("logout-timeout",
+ NULL,
+ NULL,
+ -1,
+ G_MAXLONG,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_COMMAND,
+ g_param_spec_string ("logout-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_CYCLE_TIMEOUT,
+ g_param_spec_long ("cycle-timeout",
+ NULL,
+ NULL,
+ 10000,
+ G_MAXLONG,
+ 300000,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_THROTTLED,
+ g_param_spec_boolean ("throttled",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (GSManagerPrivate));
+}
+
+static void
+on_bg_changed (MateBG *bg,
+ GSManager *manager)
+{
+ gs_debug ("background changed");
+}
+
+static void
+mateconf_changed_callback (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ GSManager *manager)
+{
+ mate_bg_load_from_preferences (manager->priv->bg,
+ manager->priv->client);
+}
+
+static void
+watch_bg_preferences (GSManager *manager)
+{
+ g_assert (manager->priv->bg_notify_id == 0);
+
+ mateconf_client_add_dir (manager->priv->client,
+ MATE_BG_KEY_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE,
+ NULL);
+ manager->priv->bg_notify_id = mateconf_client_notify_add (manager->priv->client,
+ MATE_BG_KEY_DIR,
+ (MateConfClientNotifyFunc)mateconf_changed_callback,
+ manager,
+ NULL,
+ NULL);
+}
+
+static MateConfClient *
+get_mateconf_client (void)
+{
+ MateConfClient *client;
+ GSList *addresses;
+ GError *error;
+ MateConfEngine *engine;
+
+ client = NULL;
+ addresses = NULL;
+
+ addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.mandatory");
+ addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.system");
+ addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.defaults");
+ addresses = g_slist_reverse (addresses);
+
+ error = NULL;
+ engine = mateconf_engine_get_for_addresses (addresses, &error);
+ if (engine == NULL)
+ {
+ gs_debug ("Unable to get mateconf engine for addresses: %s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ client = mateconf_client_get_for_engine (engine);
+ }
+
+ g_slist_free (addresses);
+
+ return client;
+}
+
+static void
+gs_manager_init (GSManager *manager)
+{
+ manager->priv = GS_MANAGER_GET_PRIVATE (manager);
+
+ manager->priv->fade = gs_fade_new ();
+ manager->priv->grab = gs_grab_new ();
+ manager->priv->theme_manager = gs_theme_manager_new ();
+
+ manager->priv->client = get_mateconf_client ();
+ if (manager->priv->client != NULL)
+ {
+ manager->priv->bg = mate_bg_new ();
+
+ g_signal_connect (manager->priv->bg,
+ "changed",
+ G_CALLBACK (on_bg_changed),
+ manager);
+ watch_bg_preferences (manager);
+
+ mate_bg_load_from_preferences (manager->priv->bg, manager->priv->client);
+ }
+}
+
+static void
+remove_timers (GSManager *manager)
+{
+ remove_lock_timer (manager);
+ remove_cycle_timer (manager);
+}
+
+static void
+remove_unfade_idle (GSManager *manager)
+{
+ if (manager->priv->unfade_idle_id > 0)
+ {
+ g_source_remove (manager->priv->unfade_idle_id);
+ manager->priv->unfade_idle_id = 0;
+ }
+}
+
+
+static gboolean
+window_deactivated_idle (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ /* don't deactivate directly but only emit a signal
+ so that we let the parent deactivate */
+ g_signal_emit (manager, signals [DEACTIVATED], 0);
+
+ return FALSE;
+}
+
+static void
+window_deactivated_cb (GSWindow *window,
+ GSManager *manager)
+{
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_idle_add ((GSourceFunc)window_deactivated_idle, manager);
+}
+
+static GSWindow *
+find_window_at_pointer (GSManager *manager)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ int monitor;
+ int x, y;
+ GSWindow *window;
+ int screen_num;
+ GSList *l;
+
+ display = gdk_display_get_default ();
+ gdk_display_get_pointer (display, &screen, &x, &y, NULL);
+ monitor = gdk_screen_get_monitor_at_point (screen, x, y);
+ screen_num = gdk_screen_get_number (screen);
+
+ /* Find the gs-window that is on that screen */
+ window = NULL;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ GSWindow *win = GS_WINDOW (l->data);
+ if (gs_window_get_screen (win) == screen
+ && gs_window_get_monitor (win) == monitor)
+ {
+ window = win;
+ }
+ }
+
+ if (window == NULL)
+ {
+ gs_debug ("WARNING: Could not find the GSWindow for screen %d", screen_num);
+ /* take the first one */
+ window = manager->priv->windows->data;
+ }
+ else
+ {
+ gs_debug ("Requesting unlock for screen %d", screen_num);
+ }
+
+ return window;
+}
+
+void
+gs_manager_show_message (GSManager *manager,
+ const char *summary,
+ const char *body,
+ const char *icon)
+{
+ GSWindow *window;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ /* Find the GSWindow that contains the pointer */
+ window = find_window_at_pointer (manager);
+ gs_window_show_message (window, summary, body, icon);
+
+ gs_manager_request_unlock (manager);
+}
+
+static gboolean
+manager_maybe_grab_window (GSManager *manager,
+ GSWindow *window)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ int monitor;
+ int x, y;
+ gboolean grabbed;
+
+ display = gdk_display_get_default ();
+ gdk_display_get_pointer (display, &screen, &x, &y, NULL);
+ monitor = gdk_screen_get_monitor_at_point (screen, x, y);
+
+ gdk_flush ();
+ grabbed = FALSE;
+ if (gs_window_get_screen (window) == screen
+ && gs_window_get_monitor (window) == monitor)
+ {
+ gs_debug ("Moving grab to %p", window);
+ gs_grab_move_to_window (manager->priv->grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+ grabbed = TRUE;
+ }
+
+ return grabbed;
+}
+
+static void
+window_grab_broken_cb (GSWindow *window,
+ GdkEventGrabBroken *event,
+ GSManager *manager)
+{
+ gs_debug ("GRAB BROKEN!");
+ if (event->keyboard)
+ {
+ gs_grab_keyboard_reset (manager->priv->grab);
+ }
+ else
+ {
+ gs_grab_mouse_reset (manager->priv->grab);
+ }
+}
+
+static gboolean
+unfade_idle (GSManager *manager)
+{
+ gs_debug ("resetting fade");
+ gs_fade_reset (manager->priv->fade);
+ manager->priv->unfade_idle_id = 0;
+ return FALSE;
+}
+
+
+static void
+add_unfade_idle (GSManager *manager)
+{
+ remove_unfade_idle (manager);
+ manager->priv->unfade_idle_id = g_timeout_add (500, (GSourceFunc)unfade_idle, manager);
+}
+
+static gboolean
+window_map_event_cb (GSWindow *window,
+ GdkEvent *event,
+ GSManager *manager)
+{
+ gs_debug ("Handling window map_event event");
+
+ manager_maybe_grab_window (manager, window);
+
+ manager_maybe_start_job_for_window (manager, window);
+
+ return FALSE;
+}
+
+static void
+window_map_cb (GSWindow *window,
+ GSManager *manager)
+{
+ gs_debug ("Handling window map event");
+}
+
+static void
+window_unmap_cb (GSWindow *window,
+ GSManager *manager)
+{
+ gs_debug ("window unmapped!");
+}
+
+static void
+apply_background_to_window (GSManager *manager,
+ GSWindow *window)
+{
+ GdkPixmap *pixmap;
+ GdkScreen *screen;
+ int width;
+ int height;
+
+ if (manager->priv->bg == NULL)
+ {
+ gs_debug ("No background available");
+ gs_window_set_background_pixmap (window, NULL);
+ }
+
+ screen = gs_window_get_screen (window);
+ width = gdk_screen_get_width (screen);
+ height = gdk_screen_get_height (screen);
+ gs_debug ("Creating pixmap background w:%d h:%d", width, height);
+ pixmap = mate_bg_create_pixmap (manager->priv->bg,
+ gs_window_get_gdk_window (window),
+ width,
+ height,
+ FALSE);
+ gs_window_set_background_pixmap (window, pixmap);
+ g_object_unref (pixmap);
+}
+
+static void
+manager_show_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ apply_background_to_window (manager, window);
+
+ job = gs_job_new_for_widget (gs_window_get_drawing_area (window));
+
+ manager_select_theme_for_job (manager, job);
+ manager_add_job_for_window (manager, window, job);
+
+ manager->priv->activate_time = time (NULL);
+
+ if (manager->priv->lock_timeout >= 0)
+ {
+ remove_lock_timer (manager);
+ add_lock_timer (manager, manager->priv->lock_timeout);
+ }
+
+ if (manager->priv->cycle_timeout >= 10000)
+ {
+ remove_cycle_timer (manager);
+ add_cycle_timer (manager, manager->priv->cycle_timeout);
+ }
+
+ add_unfade_idle (manager);
+
+ /* FIXME: only emit signal once */
+ g_signal_emit (manager, signals [ACTIVATED], 0);
+}
+
+static void
+window_show_cb (GSWindow *window,
+ GSManager *manager)
+{
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Handling window show");
+ manager_show_window (manager, window);
+}
+
+static void
+maybe_set_window_throttle (GSManager *manager,
+ GSWindow *window,
+ gboolean throttled)
+{
+ if (throttled)
+ {
+ manager_maybe_stop_job_for_window (manager, window);
+ }
+ else
+ {
+ manager_maybe_start_job_for_window (manager, window);
+ }
+}
+
+static void
+window_obscured_cb (GSWindow *window,
+ GParamSpec *pspec,
+ GSManager *manager)
+{
+ gboolean obscured;
+
+ obscured = gs_window_is_obscured (window);
+ gs_debug ("Handling window obscured: %s", obscured ? "obscured" : "unobscured");
+
+ maybe_set_window_throttle (manager, window, obscured);
+
+ if (! obscured)
+ {
+ gs_manager_request_unlock (manager);
+ }
+}
+
+static void
+handle_window_dialog_up (GSManager *manager,
+ GSWindow *window)
+{
+ GSList *l;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ gs_debug ("Handling dialog up");
+
+ g_signal_emit (manager, signals [AUTH_REQUEST_BEGIN], 0);
+
+ manager->priv->dialog_up = TRUE;
+ /* Make all other windows insensitive so we don't get events */
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ if (l->data != window)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (l->data), FALSE);
+ }
+ }
+
+ /* Move keyboard and mouse grabs so dialog can be used */
+ gs_grab_move_to_window (manager->priv->grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+
+ /* Release the pointer grab while dialog is up so that
+ the dialog can be used. We'll regrab it when the dialog goes down. */
+ gs_grab_release_mouse (manager->priv->grab);
+
+ if (! manager->priv->throttled)
+ {
+ gs_debug ("Suspending jobs");
+
+ manager_suspend_jobs (manager);
+ }
+}
+
+static void
+handle_window_dialog_down (GSManager *manager,
+ GSWindow *window)
+{
+ GSList *l;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ gs_debug ("Handling dialog down");
+
+ /* Regrab the mouse */
+ gs_grab_move_to_window (manager->priv->grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+
+ /* Make all windows sensitive so we get events */
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (l->data), TRUE);
+ }
+
+ manager->priv->dialog_up = FALSE;
+
+ if (! manager->priv->throttled)
+ {
+ manager_resume_jobs (manager);
+ }
+
+ g_signal_emit (manager, signals [AUTH_REQUEST_END], 0);
+}
+
+static void
+window_dialog_up_changed_cb (GSWindow *window,
+ GParamSpec *pspec,
+ GSManager *manager)
+{
+ gboolean up;
+
+ up = gs_window_is_dialog_up (window);
+ gs_debug ("Handling window dialog up changed: %s", up ? "up" : "down");
+ if (up)
+ {
+ handle_window_dialog_up (manager, window);
+ }
+ else
+ {
+ handle_window_dialog_down (manager, window);
+ }
+}
+
+static gboolean
+window_activity_cb (GSWindow *window,
+ GSManager *manager)
+{
+ gboolean handled;
+
+ handled = gs_manager_request_unlock (manager);
+
+ return handled;
+}
+
+static void
+disconnect_window_signals (GSManager *manager,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (window, window_deactivated_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_activity_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_show_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_map_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_map_event_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_obscured_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_dialog_up_changed_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_unmap_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_grab_broken_cb, manager);
+}
+
+static void
+window_destroyed_cb (GtkWindow *window,
+ GSManager *manager)
+{
+ disconnect_window_signals (manager, GS_WINDOW (window));
+}
+
+static void
+connect_window_signals (GSManager *manager,
+ GSWindow *window)
+{
+ g_signal_connect_object (window, "destroy",
+ G_CALLBACK (window_destroyed_cb), manager, 0);
+ g_signal_connect_object (window, "activity",
+ G_CALLBACK (window_activity_cb), manager, 0);
+ g_signal_connect_object (window, "deactivated",
+ G_CALLBACK (window_deactivated_cb), manager, 0);
+ g_signal_connect_object (window, "show",
+ G_CALLBACK (window_show_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "map",
+ G_CALLBACK (window_map_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "map_event",
+ G_CALLBACK (window_map_event_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "notify::obscured",
+ G_CALLBACK (window_obscured_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "notify::dialog-up",
+ G_CALLBACK (window_dialog_up_changed_cb), manager, 0);
+ g_signal_connect_object (window, "unmap",
+ G_CALLBACK (window_unmap_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "grab_broken_event",
+ G_CALLBACK (window_grab_broken_cb), manager, G_CONNECT_AFTER);
+}
+
+static void
+gs_manager_create_window_for_monitor (GSManager *manager,
+ GdkScreen *screen,
+ int monitor)
+{
+ GSWindow *window;
+ GdkRectangle rect;
+
+ gdk_screen_get_monitor_geometry (screen, monitor, &rect);
+
+ gs_debug ("Creating window for monitor %d [%d,%d] (%dx%d)",
+ monitor, rect.x, rect.y, rect.width, rect.height);
+
+ window = gs_window_new (screen, monitor, manager->priv->lock_active);
+
+ gs_window_set_user_switch_enabled (window, manager->priv->user_switch_enabled);
+ gs_window_set_logout_enabled (window, manager->priv->logout_enabled);
+ gs_window_set_logout_timeout (window, manager->priv->logout_timeout);
+ gs_window_set_logout_command (window, manager->priv->logout_command);
+ gs_window_set_keyboard_enabled (window, manager->priv->keyboard_enabled);
+ gs_window_set_keyboard_command (window, manager->priv->keyboard_command);
+ gs_window_set_status_message (window, manager->priv->status_message);
+
+ connect_window_signals (manager, window);
+
+ manager->priv->windows = g_slist_append (manager->priv->windows, window);
+
+ if (manager->priv->active && !manager->priv->fading)
+ {
+ gtk_widget_show (GTK_WIDGET (window));
+ }
+}
+
+static void
+on_screen_monitors_changed (GdkScreen *screen,
+ GSManager *manager)
+{
+ GSList *l;
+ int n_monitors;
+ int n_windows;
+ int i;
+
+ n_monitors = gdk_screen_get_n_monitors (screen);
+ n_windows = g_slist_length (manager->priv->windows);
+
+ gs_debug ("Monitors changed for screen %d: num=%d",
+ gdk_screen_get_number (screen),
+ n_monitors);
+
+ if (n_monitors > n_windows)
+ {
+
+ /* Tear down unlock dialog in case we want to move it
+ * to a new monitor
+ */
+ l = manager->priv->windows;
+ while (l != NULL)
+ {
+ gs_window_cancel_unlock_request (GS_WINDOW (l->data));
+ l = l->next;
+ }
+
+ /* add more windows */
+ for (i = n_windows; i < n_monitors; i++)
+ {
+ gs_manager_create_window_for_monitor (manager, screen, i);
+ }
+
+ /* And put unlock dialog up where ever it's supposed to be
+ */
+ gs_manager_request_unlock (manager);
+ }
+ else
+ {
+
+ gdk_x11_grab_server ();
+
+ /* remove the extra windows */
+ l = manager->priv->windows;
+ while (l != NULL)
+ {
+ GdkScreen *this_screen;
+ int this_monitor;
+ GSList *next = l->next;
+
+ this_screen = gs_window_get_screen (GS_WINDOW (l->data));
+ this_monitor = gs_window_get_monitor (GS_WINDOW (l->data));
+ if (this_screen == screen && this_monitor >= n_monitors)
+ {
+ manager_maybe_stop_job_for_window (manager, GS_WINDOW (l->data));
+ g_hash_table_remove (manager->priv->jobs, l->data);
+ gs_window_destroy (GS_WINDOW (l->data));
+ manager->priv->windows = g_slist_delete_link (manager->priv->windows, l);
+ }
+ l = next;
+ }
+
+ /* make sure there is a lock dialog on a connected monitor,
+ * and that the keyboard is still properly grabbed after all
+ * the windows above got destroyed*/
+ if (n_windows > n_monitors)
+ {
+ gs_manager_request_unlock (manager);
+ }
+
+ gdk_flush ();
+ gdk_x11_ungrab_server ();
+ }
+}
+
+static void
+gs_manager_destroy_windows (GSManager *manager)
+{
+ GdkDisplay *display;
+ GSList *l;
+ int n_screens;
+ int i;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->windows == NULL)
+ {
+ return;
+ }
+
+ display = gdk_display_get_default ();
+
+ n_screens = gdk_display_get_n_screens (display);
+
+ for (i = 0; i < n_screens; i++)
+ {
+ g_signal_handlers_disconnect_by_func (gdk_display_get_screen (display, i),
+ on_screen_monitors_changed,
+ manager);
+ }
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_destroy (l->data);
+ }
+ g_slist_free (manager->priv->windows);
+ manager->priv->windows = NULL;
+}
+
+static void
+gs_manager_finalize (GObject *object)
+{
+ GSManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_MANAGER (object));
+
+ manager = GS_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ if (manager->priv->bg_notify_id != 0)
+ {
+ mateconf_client_remove_dir (manager->priv->client,
+ MATE_BG_KEY_DIR,
+ NULL);
+ mateconf_client_notify_remove (manager->priv->client,
+ manager->priv->bg_notify_id);
+ manager->priv->bg_notify_id = 0;
+ }
+ if (manager->priv->bg != NULL)
+ {
+ g_object_unref (manager->priv->bg);
+ }
+ if (manager->priv->client != NULL)
+ {
+ g_object_unref (manager->priv->client);
+ }
+
+ free_themes (manager);
+ g_free (manager->priv->logout_command);
+ g_free (manager->priv->keyboard_command);
+ g_free (manager->priv->status_message);
+
+ remove_unfade_idle (manager);
+ remove_timers (manager);
+
+ gs_grab_release (manager->priv->grab);
+
+ manager_stop_jobs (manager);
+
+ gs_manager_destroy_windows (manager);
+
+ manager->priv->active = FALSE;
+ manager->priv->activate_time = 0;
+ manager->priv->lock_enabled = FALSE;
+
+ g_object_unref (manager->priv->fade);
+ g_object_unref (manager->priv->grab);
+ g_object_unref (manager->priv->theme_manager);
+
+ G_OBJECT_CLASS (gs_manager_parent_class)->finalize (object);
+}
+
+static void
+gs_manager_create_windows_for_screen (GSManager *manager,
+ GdkScreen *screen)
+{
+ int n_monitors;
+ int i;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ g_object_ref (manager);
+ g_object_ref (screen);
+
+ n_monitors = gdk_screen_get_n_monitors (screen);
+
+ gs_debug ("Creating %d windows for screen %d", n_monitors, gdk_screen_get_number (screen));
+
+ for (i = 0; i < n_monitors; i++)
+ {
+ gs_manager_create_window_for_monitor (manager, screen, i);
+ }
+
+ g_object_unref (screen);
+ g_object_unref (manager);
+}
+
+static void
+gs_manager_create_windows (GSManager *manager)
+{
+ GdkDisplay *display;
+ int n_screens;
+ int i;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_assert (manager->priv->windows == NULL);
+
+ display = gdk_display_get_default ();
+ n_screens = gdk_display_get_n_screens (display);
+
+ for (i = 0; i < n_screens; i++)
+ {
+ g_signal_connect (gdk_display_get_screen (display, i),
+ "monitors-changed",
+ G_CALLBACK (on_screen_monitors_changed),
+ manager);
+
+ gs_manager_create_windows_for_screen (manager, gdk_display_get_screen (display, i));
+ }
+}
+
+GSManager *
+gs_manager_new (void)
+{
+ GObject *manager;
+
+ manager = g_object_new (GS_TYPE_MANAGER, NULL);
+
+ return GS_MANAGER (manager);
+}
+
+static void
+show_windows (GSList *windows)
+{
+ GSList *l;
+
+ for (l = windows; l; l = l->next)
+ {
+ gtk_widget_show (GTK_WIDGET (l->data));
+ }
+}
+
+static void
+remove_job (GSJob *job)
+{
+ if (job == NULL)
+ {
+ return;
+ }
+
+ gs_job_stop (job);
+ g_object_unref (job);
+}
+
+static void
+fade_done_cb (GSFade *fade,
+ GSManager *manager)
+{
+ gs_debug ("fade completed, showing windows");
+ show_windows (manager->priv->windows);
+ manager->priv->fading = FALSE;
+}
+
+static gboolean
+gs_manager_activate (GSManager *manager)
+{
+ gboolean do_fade;
+ gboolean res;
+
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (manager->priv->active)
+ {
+ gs_debug ("Trying to activate manager when already active");
+ return FALSE;
+ }
+
+ res = gs_grab_grab_root (manager->priv->grab, FALSE);
+ if (! res)
+ {
+ return FALSE;
+ }
+
+ if (manager->priv->windows == NULL)
+ {
+ gs_manager_create_windows (GS_MANAGER (manager));
+ }
+
+ manager->priv->jobs = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)remove_job);
+
+ manager->priv->active = TRUE;
+
+ /* fade to black and show windows */
+ do_fade = TRUE;
+ if (do_fade)
+ {
+ manager->priv->fading = TRUE;
+ gs_debug ("fading out");
+ gs_fade_async (manager->priv->fade,
+ FADE_TIMEOUT,
+ (GSFadeDoneFunc)fade_done_cb,
+ manager);
+
+ while (manager->priv->fading)
+ {
+ gtk_main_iteration ();
+ }
+ }
+ else
+ {
+ show_windows (manager->priv->windows);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gs_manager_deactivate (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (! manager->priv->active)
+ {
+ gs_debug ("Trying to deactivate a screensaver that is not active");
+ return FALSE;
+ }
+
+ remove_unfade_idle (manager);
+ gs_fade_reset (manager->priv->fade);
+ remove_timers (manager);
+
+ gs_grab_release (manager->priv->grab);
+
+ manager_stop_jobs (manager);
+
+ gs_manager_destroy_windows (manager);
+
+ /* reset state */
+ manager->priv->active = FALSE;
+ manager->priv->activate_time = 0;
+ manager->priv->lock_active = FALSE;
+ manager->priv->dialog_up = FALSE;
+ manager->priv->fading = FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gs_manager_set_active (GSManager *manager,
+ gboolean active)
+{
+ gboolean res;
+
+ if (active)
+ {
+ res = gs_manager_activate (manager);
+ }
+ else
+ {
+ res = gs_manager_deactivate (manager);
+ }
+
+ return res;
+}
+
+gboolean
+gs_manager_get_active (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ return manager->priv->active;
+}
+
+gboolean
+gs_manager_request_unlock (GSManager *manager)
+{
+ GSWindow *window;
+
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (! manager->priv->active)
+ {
+ gs_debug ("Request unlock but manager is not active");
+ return FALSE;
+ }
+
+ if (manager->priv->dialog_up)
+ {
+ gs_debug ("Request unlock but dialog is already up");
+ return FALSE;
+ }
+
+ if (manager->priv->fading)
+ {
+ gs_debug ("Request unlock so finishing fade");
+ gs_fade_finish (manager->priv->fade);
+ }
+
+ if (manager->priv->windows == NULL)
+ {
+ gs_debug ("We don't have any windows!");
+ return FALSE;
+ }
+
+ /* Find the GSWindow that contains the pointer */
+ window = find_window_at_pointer (manager);
+ gs_window_request_unlock (window);
+
+ return TRUE;
+}
+
+void
+gs_manager_cancel_unlock_request (GSManager *manager)
+{
+ GSList *l;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_cancel_unlock_request (l->data);
+ }
+}
diff --git a/src/gs-manager.c.orig b/src/gs-manager.c.orig
new file mode 100644
index 0000000..47c5005
--- /dev/null
+++ b/src/gs-manager.c.orig
@@ -0,0 +1,2030 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2008 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <time.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+
+#include <mateconf/mateconf-engine.h>
+#include <mateconf/mateconf-client.h>
+
+#define MATE_DESKTOP_USE_UNSTABLE_API
+#include <libmateui/mate-bg.h>
+
+#include "gs-prefs.h" /* for GSSaverMode */
+
+#include "gs-manager.h"
+#include "gs-window.h"
+#include "gs-theme-manager.h"
+#include "gs-job.h"
+#include "gs-grab.h"
+#include "gs-fade.h"
+#include "gs-debug.h"
+
+static void gs_manager_class_init (GSManagerClass *klass);
+static void gs_manager_init (GSManager *manager);
+static void gs_manager_finalize (GObject *object);
+
+#define GS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_MANAGER, GSManagerPrivate))
+
+struct GSManagerPrivate
+{
+ GSList *windows;
+ GHashTable *jobs;
+
+ GSThemeManager *theme_manager;
+ MateConfClient *client;
+ MateBG *bg;
+ guint bg_notify_id;
+
+ /* Policy */
+ glong lock_timeout;
+ glong cycle_timeout;
+ glong logout_timeout;
+
+ guint lock_enabled : 1;
+ guint logout_enabled : 1;
+ guint keyboard_enabled : 1;
+ guint user_switch_enabled : 1;
+ guint throttled : 1;
+
+ char *logout_command;
+ char *keyboard_command;
+
+ char *status_message;
+
+ /* State */
+ guint active : 1;
+ guint lock_active : 1;
+
+ guint fading : 1;
+ guint dialog_up : 1;
+
+ time_t activate_time;
+
+ guint lock_timeout_id;
+ guint cycle_timeout_id;
+
+ GSList *themes;
+ GSSaverMode saver_mode;
+ GSGrab *grab;
+ GSFade *fade;
+ guint unfade_idle_id;
+};
+
+enum
+{
+ ACTIVATED,
+ DEACTIVATED,
+ AUTH_REQUEST_BEGIN,
+ AUTH_REQUEST_END,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_LOCK_ENABLED,
+ PROP_LOGOUT_ENABLED,
+ PROP_USER_SWITCH_ENABLED,
+ PROP_KEYBOARD_ENABLED,
+ PROP_LOCK_TIMEOUT,
+ PROP_CYCLE_TIMEOUT,
+ PROP_LOGOUT_TIMEOUT,
+ PROP_LOGOUT_COMMAND,
+ PROP_KEYBOARD_COMMAND,
+ PROP_STATUS_MESSAGE,
+ PROP_ACTIVE,
+ PROP_THROTTLED,
+};
+
+#define FADE_TIMEOUT 1000
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSManager, gs_manager, G_TYPE_OBJECT)
+
+static void
+manager_add_job_for_window (GSManager *manager,
+ GSWindow *window,
+ GSJob *job)
+{
+ if (manager->priv->jobs == NULL)
+ {
+ return;
+ }
+
+ g_hash_table_insert (manager->priv->jobs, window, job);
+}
+
+static const char *
+select_theme (GSManager *manager)
+{
+ const char *theme = NULL;
+
+ g_return_val_if_fail (manager != NULL, NULL);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), NULL);
+
+ if (manager->priv->saver_mode == GS_MODE_BLANK_ONLY)
+ {
+ return NULL;
+ }
+
+ if (manager->priv->themes)
+ {
+ int number = 0;
+
+ if (manager->priv->saver_mode == GS_MODE_RANDOM)
+ {
+ g_random_set_seed (time (NULL));
+ number = g_random_int_range (0, g_slist_length (manager->priv->themes));
+ }
+ theme = g_slist_nth_data (manager->priv->themes, number);
+ }
+
+ return theme;
+}
+
+static GSJob *
+lookup_job_for_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ if (manager->priv->jobs == NULL)
+ {
+ return NULL;
+ }
+
+ job = g_hash_table_lookup (manager->priv->jobs, window);
+
+ return job;
+}
+
+static void
+manager_maybe_stop_job_for_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ job = lookup_job_for_window (manager, window);
+
+ if (job == NULL)
+ {
+ gs_debug ("Job not found for window");
+ return;
+ }
+
+ gs_job_stop (job);
+}
+
+static void
+manager_maybe_start_job_for_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ job = lookup_job_for_window (manager, window);
+
+ if (job == NULL)
+ {
+ gs_debug ("Job not found for window");
+ return;
+ }
+
+ if (! manager->priv->dialog_up)
+ {
+ if (! manager->priv->throttled)
+ {
+ if (! gs_job_is_running (job))
+ {
+ if (! gs_window_is_obscured (window))
+ {
+ gs_debug ("Starting job for window");
+ gs_job_start (job);
+ }
+ else
+ {
+ gs_debug ("Window is obscured deferring start of job");
+ }
+ }
+ else
+ {
+ gs_debug ("Not starting job because job is running");
+ }
+ }
+ else
+ {
+ gs_debug ("Not starting job because throttled");
+ }
+ }
+ else
+ {
+ gs_debug ("Not starting job because dialog is up");
+ }
+}
+
+static void
+manager_select_theme_for_job (GSManager *manager,
+ GSJob *job)
+{
+ const char *theme;
+
+ theme = select_theme (manager);
+
+ if (theme != NULL)
+ {
+ GSThemeInfo *info;
+ const char *command;
+
+ command = NULL;
+
+ info = gs_theme_manager_lookup_theme_info (manager->priv->theme_manager, theme);
+ if (info != NULL)
+ {
+ command = gs_theme_info_get_exec (info);
+ }
+ else
+ {
+ gs_debug ("Could not find information for theme: %s",
+ theme);
+ }
+
+ gs_job_set_command (job, command);
+
+
+ if (info != NULL)
+ {
+ gs_theme_info_unref (info);
+ }
+ }
+ else
+ {
+ gs_job_set_command (job, NULL);
+ }
+}
+
+static void
+cycle_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ gs_job_stop (job);
+ manager_select_theme_for_job (manager, job);
+ manager_maybe_start_job_for_window (manager, window);
+}
+
+static void
+manager_cycle_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) cycle_job, manager);
+ }
+}
+
+static void
+throttle_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ if (manager->priv->throttled)
+ {
+ gs_job_stop (job);
+ }
+ else
+ {
+ manager_maybe_start_job_for_window (manager, window);
+ }
+}
+
+static void
+manager_throttle_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) throttle_job, manager);
+ }
+}
+
+static void
+resume_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ if (gs_job_is_running (job))
+ {
+ gs_job_suspend (job, FALSE);
+ }
+ else
+ {
+ manager_maybe_start_job_for_window (manager, window);
+ }
+}
+
+static void
+manager_resume_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) resume_job, manager);
+ }
+}
+
+static void
+suspend_job (GSWindow *window,
+ GSJob *job,
+ GSManager *manager)
+{
+ gs_job_suspend (job, TRUE);
+}
+
+static void
+manager_suspend_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_foreach (manager->priv->jobs, (GHFunc) suspend_job, manager);
+ }
+}
+
+static void
+manager_stop_jobs (GSManager *manager)
+{
+ if (manager->priv->jobs != NULL)
+ {
+ g_hash_table_destroy (manager->priv->jobs);
+
+ }
+ manager->priv->jobs = NULL;
+}
+
+void
+gs_manager_set_mode (GSManager *manager,
+ GSSaverMode mode)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ manager->priv->saver_mode = mode;
+}
+
+static void
+free_themes (GSManager *manager)
+{
+ if (manager->priv->themes)
+ {
+ g_slist_foreach (manager->priv->themes, (GFunc)g_free, NULL);
+ g_slist_free (manager->priv->themes);
+ }
+}
+
+void
+gs_manager_set_themes (GSManager *manager,
+ GSList *themes)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ free_themes (manager);
+ manager->priv->themes = NULL;
+
+ for (l = themes; l; l = l->next)
+ {
+ manager->priv->themes = g_slist_append (manager->priv->themes, g_strdup (l->data));
+ }
+}
+
+void
+gs_manager_set_throttled (GSManager *manager,
+ gboolean throttled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->throttled != throttled)
+ {
+ GSList *l;
+
+ manager->priv->throttled = throttled;
+
+ if (! manager->priv->dialog_up)
+ {
+
+ manager_throttle_jobs (manager);
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_clear (l->data);
+ }
+ }
+ }
+}
+
+void
+gs_manager_get_lock_active (GSManager *manager,
+ gboolean *lock_active)
+{
+ if (lock_active != NULL)
+ {
+ *lock_active = FALSE;
+ }
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ *lock_active = manager->priv->lock_active;
+}
+
+void
+gs_manager_set_lock_active (GSManager *manager,
+ gboolean lock_active)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ gs_debug ("Setting lock active: %d", lock_active);
+
+ if (manager->priv->lock_active != lock_active)
+ {
+ GSList *l;
+
+ manager->priv->lock_active = lock_active;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_lock_enabled (l->data, lock_active);
+ }
+ }
+}
+
+void
+gs_manager_get_lock_enabled (GSManager *manager,
+ gboolean *lock_enabled)
+{
+ if (lock_enabled != NULL)
+ {
+ *lock_enabled = FALSE;
+ }
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ *lock_enabled = manager->priv->lock_enabled;
+}
+
+void
+gs_manager_set_lock_enabled (GSManager *manager,
+ gboolean lock_enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->lock_enabled != lock_enabled)
+ {
+ manager->priv->lock_enabled = lock_enabled;
+ }
+}
+
+void
+gs_manager_set_logout_enabled (GSManager *manager,
+ gboolean logout_enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->logout_enabled != logout_enabled)
+ {
+ GSList *l;
+
+ manager->priv->logout_enabled = logout_enabled;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_logout_enabled (l->data, logout_enabled);
+ }
+ }
+}
+
+void
+gs_manager_set_keyboard_enabled (GSManager *manager,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->keyboard_enabled != enabled)
+ {
+ GSList *l;
+
+ manager->priv->keyboard_enabled = enabled;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_keyboard_enabled (l->data, enabled);
+ }
+ }
+}
+
+void
+gs_manager_set_user_switch_enabled (GSManager *manager,
+ gboolean user_switch_enabled)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->user_switch_enabled != user_switch_enabled)
+ {
+ GSList *l;
+
+ manager->priv->user_switch_enabled = user_switch_enabled;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_user_switch_enabled (l->data, user_switch_enabled);
+ }
+ }
+}
+
+static gboolean
+activate_lock_timeout (GSManager *manager)
+{
+ if (manager->priv->lock_enabled)
+ {
+ gs_manager_set_lock_active (manager, TRUE);
+ }
+
+ manager->priv->lock_timeout_id = 0;
+
+ return FALSE;
+}
+
+static void
+remove_lock_timer (GSManager *manager)
+{
+ if (manager->priv->lock_timeout_id != 0)
+ {
+ g_source_remove (manager->priv->lock_timeout_id);
+ manager->priv->lock_timeout_id = 0;
+ }
+}
+
+static void
+add_lock_timer (GSManager *manager,
+ glong timeout)
+{
+ manager->priv->lock_timeout_id = g_timeout_add (timeout,
+ (GSourceFunc)activate_lock_timeout,
+ manager);
+}
+
+void
+gs_manager_set_lock_timeout (GSManager *manager,
+ glong lock_timeout)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->lock_timeout != lock_timeout)
+ {
+
+ manager->priv->lock_timeout = lock_timeout;
+
+ if (manager->priv->active
+ && ! manager->priv->lock_active
+ && (lock_timeout >= 0))
+ {
+
+ glong elapsed = (time (NULL) - manager->priv->activate_time) * 1000;
+
+ remove_lock_timer (manager);
+
+ if (elapsed >= lock_timeout)
+ {
+ activate_lock_timeout (manager);
+ }
+ else
+ {
+ add_lock_timer (manager, lock_timeout - elapsed);
+ }
+ }
+ }
+}
+
+void
+gs_manager_set_logout_timeout (GSManager *manager,
+ glong logout_timeout)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->logout_timeout != logout_timeout)
+ {
+ GSList *l;
+
+ manager->priv->logout_timeout = logout_timeout;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_logout_timeout (l->data, logout_timeout);
+ }
+ }
+}
+
+void
+gs_manager_set_logout_command (GSManager *manager,
+ const char *command)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_free (manager->priv->logout_command);
+
+ if (command)
+ {
+ manager->priv->logout_command = g_strdup (command);
+ }
+ else
+ {
+ manager->priv->logout_command = NULL;
+ }
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_logout_command (l->data, manager->priv->logout_command);
+ }
+}
+
+void
+gs_manager_set_keyboard_command (GSManager *manager,
+ const char *command)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_free (manager->priv->keyboard_command);
+
+ if (command)
+ {
+ manager->priv->keyboard_command = g_strdup (command);
+ }
+ else
+ {
+ manager->priv->keyboard_command = NULL;
+ }
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_keyboard_command (l->data, manager->priv->keyboard_command);
+ }
+}
+
+void
+gs_manager_set_status_message (GSManager *manager,
+ const char *status_message)
+{
+ GSList *l;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_free (manager->priv->status_message);
+
+ manager->priv->status_message = g_strdup (status_message);
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_set_status_message (l->data, manager->priv->status_message);
+ }
+}
+
+gboolean
+gs_manager_cycle (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ gs_debug ("cycling jobs");
+
+ if (! manager->priv->active)
+ {
+ return FALSE;
+ }
+
+ if (manager->priv->dialog_up)
+ {
+ return FALSE;
+ }
+
+ if (manager->priv->throttled)
+ {
+ return FALSE;
+ }
+
+ manager_cycle_jobs (manager);
+
+ return TRUE;
+}
+
+static gboolean
+cycle_timeout (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (! manager->priv->dialog_up)
+ {
+ gs_manager_cycle (manager);
+ }
+
+ return TRUE;
+}
+
+static void
+remove_cycle_timer (GSManager *manager)
+{
+ if (manager->priv->cycle_timeout_id != 0)
+ {
+ g_source_remove (manager->priv->cycle_timeout_id);
+ manager->priv->cycle_timeout_id = 0;
+ }
+}
+
+static void
+add_cycle_timer (GSManager *manager,
+ glong timeout)
+{
+ manager->priv->cycle_timeout_id = g_timeout_add (timeout,
+ (GSourceFunc)cycle_timeout,
+ manager);
+}
+
+void
+gs_manager_set_cycle_timeout (GSManager *manager,
+ glong cycle_timeout)
+{
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->cycle_timeout != cycle_timeout)
+ {
+
+ manager->priv->cycle_timeout = cycle_timeout;
+
+ if (manager->priv->active && (cycle_timeout >= 0))
+ {
+ glong timeout;
+ glong elapsed = (time (NULL) - manager->priv->activate_time) * 1000;
+
+ remove_cycle_timer (manager);
+
+ if (elapsed >= cycle_timeout)
+ {
+ timeout = 0;
+ }
+ else
+ {
+ timeout = cycle_timeout - elapsed;
+ }
+
+ add_cycle_timer (manager, timeout);
+
+ }
+ }
+}
+
+static void
+gs_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSManager *self;
+
+ self = GS_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_THROTTLED:
+ gs_manager_set_throttled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOCK_ENABLED:
+ gs_manager_set_lock_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOCK_TIMEOUT:
+ gs_manager_set_lock_timeout (self, g_value_get_long (value));
+ break;
+ case PROP_LOGOUT_ENABLED:
+ gs_manager_set_logout_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ gs_manager_set_keyboard_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_USER_SWITCH_ENABLED:
+ gs_manager_set_user_switch_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ gs_manager_set_logout_timeout (self, g_value_get_long (value));
+ break;
+ case PROP_LOGOUT_COMMAND:
+ gs_manager_set_logout_command (self, g_value_get_string (value));
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ gs_manager_set_keyboard_command (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS_MESSAGE:
+ gs_manager_set_status_message (self, g_value_get_string (value));
+ break;
+ case PROP_CYCLE_TIMEOUT:
+ gs_manager_set_cycle_timeout (self, g_value_get_long (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSManager *self;
+
+ self = GS_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_THROTTLED:
+ g_value_set_boolean (value, self->priv->throttled);
+ break;
+ case PROP_LOCK_ENABLED:
+ g_value_set_boolean (value, self->priv->lock_enabled);
+ break;
+ case PROP_LOCK_TIMEOUT:
+ g_value_set_long (value, self->priv->lock_timeout);
+ break;
+ case PROP_LOGOUT_ENABLED:
+ g_value_set_boolean (value, self->priv->logout_enabled);
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ g_value_set_boolean (value, self->priv->keyboard_enabled);
+ break;
+ case PROP_USER_SWITCH_ENABLED:
+ g_value_set_boolean (value, self->priv->user_switch_enabled);
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ g_value_set_long (value, self->priv->logout_timeout);
+ break;
+ case PROP_LOGOUT_COMMAND:
+ g_value_set_string (value, self->priv->logout_command);
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ g_value_set_string (value, self->priv->keyboard_command);
+ break;
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ case PROP_CYCLE_TIMEOUT:
+ g_value_set_long (value, self->priv->cycle_timeout);
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, self->priv->active);
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_manager_class_init (GSManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_manager_finalize;
+ object_class->get_property = gs_manager_get_property;
+ object_class->set_property = gs_manager_set_property;
+
+ signals [ACTIVATED] =
+ g_signal_new ("activated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, activated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [DEACTIVATED] =
+ g_signal_new ("deactivated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, deactivated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [AUTH_REQUEST_BEGIN] =
+ g_signal_new ("auth-request-begin",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, auth_request_begin),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+ signals [AUTH_REQUEST_END] =
+ g_signal_new ("auth-request-end",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSManagerClass, auth_request_end),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_ACTIVE,
+ g_param_spec_boolean ("active",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_LOCK_ENABLED,
+ g_param_spec_boolean ("lock-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOCK_TIMEOUT,
+ g_param_spec_long ("lock-timeout",
+ NULL,
+ NULL,
+ -1,
+ G_MAXLONG,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_ENABLED,
+ g_param_spec_boolean ("logout-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_USER_SWITCH_ENABLED,
+ g_param_spec_boolean ("user-switch-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_TIMEOUT,
+ g_param_spec_long ("logout-timeout",
+ NULL,
+ NULL,
+ -1,
+ G_MAXLONG,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_COMMAND,
+ g_param_spec_string ("logout-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_CYCLE_TIMEOUT,
+ g_param_spec_long ("cycle-timeout",
+ NULL,
+ NULL,
+ 10000,
+ G_MAXLONG,
+ 300000,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_THROTTLED,
+ g_param_spec_boolean ("throttled",
+ NULL,
+ NULL,
+ TRUE,
+ G_PARAM_READWRITE));
+
+ g_type_class_add_private (klass, sizeof (GSManagerPrivate));
+}
+
+static void
+on_bg_changed (MateBG *bg,
+ GSManager *manager)
+{
+ gs_debug ("background changed");
+}
+
+static void
+mateconf_changed_callback (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ GSManager *manager)
+{
+ mate_bg_load_from_preferences (manager->priv->bg,
+ manager->priv->client);
+}
+
+static void
+watch_bg_preferences (GSManager *manager)
+{
+ g_assert (manager->priv->bg_notify_id == 0);
+
+ mateconf_client_add_dir (manager->priv->client,
+ MATE_BG_KEY_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE,
+ NULL);
+ manager->priv->bg_notify_id = mateconf_client_notify_add (manager->priv->client,
+ MATE_BG_KEY_DIR,
+ (MateConfClientNotifyFunc)mateconf_changed_callback,
+ manager,
+ NULL,
+ NULL);
+}
+
+static MateConfClient *
+get_mateconf_client (void)
+{
+ MateConfClient *client;
+ GSList *addresses;
+ GError *error;
+ MateConfEngine *engine;
+
+ client = NULL;
+ addresses = NULL;
+
+ addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.mandatory");
+ addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.system");
+ addresses = g_slist_prepend (addresses, "xml:merged:" SYSCONFDIR "/mateconf/mateconf.xml.defaults");
+ addresses = g_slist_reverse (addresses);
+
+ error = NULL;
+ engine = mateconf_engine_get_for_addresses (addresses, &error);
+ if (engine == NULL)
+ {
+ gs_debug ("Unable to get mateconf engine for addresses: %s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ client = mateconf_client_get_for_engine (engine);
+ }
+
+ g_slist_free (addresses);
+
+ return client;
+}
+
+static void
+gs_manager_init (GSManager *manager)
+{
+ manager->priv = GS_MANAGER_GET_PRIVATE (manager);
+
+ manager->priv->fade = gs_fade_new ();
+ manager->priv->grab = gs_grab_new ();
+ manager->priv->theme_manager = gs_theme_manager_new ();
+
+ manager->priv->client = get_mateconf_client ();
+ if (manager->priv->client != NULL)
+ {
+ manager->priv->bg = mate_bg_new ();
+
+ g_signal_connect (manager->priv->bg,
+ "changed",
+ G_CALLBACK (on_bg_changed),
+ manager);
+ watch_bg_preferences (manager);
+
+ mate_bg_load_from_preferences (manager->priv->bg, manager->priv->client);
+ }
+}
+
+static void
+remove_timers (GSManager *manager)
+{
+ remove_lock_timer (manager);
+ remove_cycle_timer (manager);
+}
+
+static void
+remove_unfade_idle (GSManager *manager)
+{
+ if (manager->priv->unfade_idle_id > 0)
+ {
+ g_source_remove (manager->priv->unfade_idle_id);
+ manager->priv->unfade_idle_id = 0;
+ }
+}
+
+
+static gboolean
+window_deactivated_idle (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ /* don't deactivate directly but only emit a signal
+ so that we let the parent deactivate */
+ g_signal_emit (manager, signals [DEACTIVATED], 0);
+
+ return FALSE;
+}
+
+static void
+window_deactivated_cb (GSWindow *window,
+ GSManager *manager)
+{
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_idle_add ((GSourceFunc)window_deactivated_idle, manager);
+}
+
+static GSWindow *
+find_window_at_pointer (GSManager *manager)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ int monitor;
+ int x, y;
+ GSWindow *window;
+ int screen_num;
+ GSList *l;
+
+ display = gdk_display_get_default ();
+ gdk_display_get_pointer (display, &screen, &x, &y, NULL);
+ monitor = gdk_screen_get_monitor_at_point (screen, x, y);
+ screen_num = gdk_screen_get_number (screen);
+
+ /* Find the gs-window that is on that screen */
+ window = NULL;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ GSWindow *win = GS_WINDOW (l->data);
+ if (gs_window_get_screen (win) == screen
+ && gs_window_get_monitor (win) == monitor)
+ {
+ window = win;
+ }
+ }
+
+ if (window == NULL)
+ {
+ gs_debug ("WARNING: Could not find the GSWindow for screen %d", screen_num);
+ /* take the first one */
+ window = manager->priv->windows->data;
+ }
+ else
+ {
+ gs_debug ("Requesting unlock for screen %d", screen_num);
+ }
+
+ return window;
+}
+
+void
+gs_manager_show_message (GSManager *manager,
+ const char *summary,
+ const char *body,
+ const char *icon)
+{
+ GSWindow *window;
+
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ /* Find the GSWindow that contains the pointer */
+ window = find_window_at_pointer (manager);
+ gs_window_show_message (window, summary, body, icon);
+
+ gs_manager_request_unlock (manager);
+}
+
+static gboolean
+manager_maybe_grab_window (GSManager *manager,
+ GSWindow *window)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ int monitor;
+ int x, y;
+ gboolean grabbed;
+
+ display = gdk_display_get_default ();
+ gdk_display_get_pointer (display, &screen, &x, &y, NULL);
+ monitor = gdk_screen_get_monitor_at_point (screen, x, y);
+
+ gdk_flush ();
+ grabbed = FALSE;
+ if (gs_window_get_screen (window) == screen
+ && gs_window_get_monitor (window) == monitor)
+ {
+ gs_debug ("Moving grab to %p", window);
+ gs_grab_move_to_window (manager->priv->grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+ grabbed = TRUE;
+ }
+
+ return grabbed;
+}
+
+static void
+window_grab_broken_cb (GSWindow *window,
+ GdkEventGrabBroken *event,
+ GSManager *manager)
+{
+ gs_debug ("GRAB BROKEN!");
+ if (event->keyboard)
+ {
+ gs_grab_keyboard_reset (manager->priv->grab);
+ }
+ else
+ {
+ gs_grab_mouse_reset (manager->priv->grab);
+ }
+}
+
+static gboolean
+unfade_idle (GSManager *manager)
+{
+ gs_debug ("resetting fade");
+ gs_fade_reset (manager->priv->fade);
+ manager->priv->unfade_idle_id = 0;
+ return FALSE;
+}
+
+
+static void
+add_unfade_idle (GSManager *manager)
+{
+ remove_unfade_idle (manager);
+ manager->priv->unfade_idle_id = g_timeout_add (500, (GSourceFunc)unfade_idle, manager);
+}
+
+static gboolean
+window_map_event_cb (GSWindow *window,
+ GdkEvent *event,
+ GSManager *manager)
+{
+ gs_debug ("Handling window map_event event");
+
+ manager_maybe_grab_window (manager, window);
+
+ manager_maybe_start_job_for_window (manager, window);
+
+ return FALSE;
+}
+
+static void
+window_map_cb (GSWindow *window,
+ GSManager *manager)
+{
+ gs_debug ("Handling window map event");
+}
+
+static void
+window_unmap_cb (GSWindow *window,
+ GSManager *manager)
+{
+ gs_debug ("window unmapped!");
+}
+
+static void
+apply_background_to_window (GSManager *manager,
+ GSWindow *window)
+{
+ GdkPixmap *pixmap;
+ GdkScreen *screen;
+ int width;
+ int height;
+
+ if (manager->priv->bg == NULL)
+ {
+ gs_debug ("No background available");
+ gs_window_set_background_pixmap (window, NULL);
+ }
+
+ screen = gs_window_get_screen (window);
+ width = gdk_screen_get_width (screen);
+ height = gdk_screen_get_height (screen);
+ gs_debug ("Creating pixmap background w:%d h:%d", width, height);
+ pixmap = mate_bg_create_pixmap (manager->priv->bg,
+ gs_window_get_gdk_window (window),
+ width,
+ height,
+ FALSE);
+ gs_window_set_background_pixmap (window, pixmap);
+ g_object_unref (pixmap);
+}
+
+static void
+manager_show_window (GSManager *manager,
+ GSWindow *window)
+{
+ GSJob *job;
+
+ apply_background_to_window (manager, window);
+
+ job = gs_job_new_for_widget (gs_window_get_drawing_area (window));
+
+ manager_select_theme_for_job (manager, job);
+ manager_add_job_for_window (manager, window, job);
+
+ manager->priv->activate_time = time (NULL);
+
+ if (manager->priv->lock_timeout >= 0)
+ {
+ remove_lock_timer (manager);
+ add_lock_timer (manager, manager->priv->lock_timeout);
+ }
+
+ if (manager->priv->cycle_timeout >= 10000)
+ {
+ remove_cycle_timer (manager);
+ add_cycle_timer (manager, manager->priv->cycle_timeout);
+ }
+
+ add_unfade_idle (manager);
+
+ /* FIXME: only emit signal once */
+ g_signal_emit (manager, signals [ACTIVATED], 0);
+}
+
+static void
+window_show_cb (GSWindow *window,
+ GSManager *manager)
+{
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+ g_return_if_fail (window != NULL);
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Handling window show");
+ manager_show_window (manager, window);
+}
+
+static void
+maybe_set_window_throttle (GSManager *manager,
+ GSWindow *window,
+ gboolean throttled)
+{
+ if (throttled)
+ {
+ manager_maybe_stop_job_for_window (manager, window);
+ }
+ else
+ {
+ manager_maybe_start_job_for_window (manager, window);
+ }
+}
+
+static void
+window_obscured_cb (GSWindow *window,
+ GParamSpec *pspec,
+ GSManager *manager)
+{
+ gboolean obscured;
+
+ obscured = gs_window_is_obscured (window);
+ gs_debug ("Handling window obscured: %s", obscured ? "obscured" : "unobscured");
+
+ maybe_set_window_throttle (manager, window, obscured);
+
+ if (! obscured)
+ {
+ gs_manager_request_unlock (manager);
+ }
+}
+
+static void
+handle_window_dialog_up (GSManager *manager,
+ GSWindow *window)
+{
+ GSList *l;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ gs_debug ("Handling dialog up");
+
+ g_signal_emit (manager, signals [AUTH_REQUEST_BEGIN], 0);
+
+ manager->priv->dialog_up = TRUE;
+ /* Make all other windows insensitive so we don't get events */
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ if (l->data != window)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (l->data), FALSE);
+ }
+ }
+
+ /* Move keyboard and mouse grabs so dialog can be used */
+ gs_grab_move_to_window (manager->priv->grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+
+ /* Release the pointer grab while dialog is up so that
+ the dialog can be used. We'll regrab it when the dialog goes down. */
+ gs_grab_release_mouse (manager->priv->grab);
+
+ if (! manager->priv->throttled)
+ {
+ gs_debug ("Suspending jobs");
+
+ manager_suspend_jobs (manager);
+ }
+}
+
+static void
+handle_window_dialog_down (GSManager *manager,
+ GSWindow *window)
+{
+ GSList *l;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ gs_debug ("Handling dialog down");
+
+ /* Regrab the mouse */
+ gs_grab_move_to_window (manager->priv->grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+
+ /* Make all windows sensitive so we get events */
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gtk_widget_set_sensitive (GTK_WIDGET (l->data), TRUE);
+ }
+
+ manager->priv->dialog_up = FALSE;
+
+ if (! manager->priv->throttled)
+ {
+ manager_resume_jobs (manager);
+ }
+
+ g_signal_emit (manager, signals [AUTH_REQUEST_END], 0);
+}
+
+static void
+window_dialog_up_changed_cb (GSWindow *window,
+ GParamSpec *pspec,
+ GSManager *manager)
+{
+ gboolean up;
+
+ up = gs_window_is_dialog_up (window);
+ gs_debug ("Handling window dialog up changed: %s", up ? "up" : "down");
+ if (up)
+ {
+ handle_window_dialog_up (manager, window);
+ }
+ else
+ {
+ handle_window_dialog_down (manager, window);
+ }
+}
+
+static gboolean
+window_activity_cb (GSWindow *window,
+ GSManager *manager)
+{
+ gboolean handled;
+
+ handled = gs_manager_request_unlock (manager);
+
+ return handled;
+}
+
+static void
+disconnect_window_signals (GSManager *manager,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (window, window_deactivated_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_activity_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_show_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_map_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_map_event_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_obscured_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_dialog_up_changed_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_unmap_cb, manager);
+ g_signal_handlers_disconnect_by_func (window, window_grab_broken_cb, manager);
+}
+
+static void
+window_destroyed_cb (GtkWindow *window,
+ GSManager *manager)
+{
+ disconnect_window_signals (manager, GS_WINDOW (window));
+}
+
+static void
+connect_window_signals (GSManager *manager,
+ GSWindow *window)
+{
+ g_signal_connect_object (window, "destroy",
+ G_CALLBACK (window_destroyed_cb), manager, 0);
+ g_signal_connect_object (window, "activity",
+ G_CALLBACK (window_activity_cb), manager, 0);
+ g_signal_connect_object (window, "deactivated",
+ G_CALLBACK (window_deactivated_cb), manager, 0);
+ g_signal_connect_object (window, "show",
+ G_CALLBACK (window_show_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "map",
+ G_CALLBACK (window_map_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "map_event",
+ G_CALLBACK (window_map_event_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "notify::obscured",
+ G_CALLBACK (window_obscured_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "notify::dialog-up",
+ G_CALLBACK (window_dialog_up_changed_cb), manager, 0);
+ g_signal_connect_object (window, "unmap",
+ G_CALLBACK (window_unmap_cb), manager, G_CONNECT_AFTER);
+ g_signal_connect_object (window, "grab_broken_event",
+ G_CALLBACK (window_grab_broken_cb), manager, G_CONNECT_AFTER);
+}
+
+static void
+gs_manager_create_window_for_monitor (GSManager *manager,
+ GdkScreen *screen,
+ int monitor)
+{
+ GSWindow *window;
+ GdkRectangle rect;
+
+ gdk_screen_get_monitor_geometry (screen, monitor, &rect);
+
+ gs_debug ("Creating window for monitor %d [%d,%d] (%dx%d)",
+ monitor, rect.x, rect.y, rect.width, rect.height);
+
+ window = gs_window_new (screen, monitor, manager->priv->lock_active);
+
+ gs_window_set_user_switch_enabled (window, manager->priv->user_switch_enabled);
+ gs_window_set_logout_enabled (window, manager->priv->logout_enabled);
+ gs_window_set_logout_timeout (window, manager->priv->logout_timeout);
+ gs_window_set_logout_command (window, manager->priv->logout_command);
+ gs_window_set_keyboard_enabled (window, manager->priv->keyboard_enabled);
+ gs_window_set_keyboard_command (window, manager->priv->keyboard_command);
+ gs_window_set_status_message (window, manager->priv->status_message);
+
+ connect_window_signals (manager, window);
+
+ manager->priv->windows = g_slist_append (manager->priv->windows, window);
+
+ if (manager->priv->active && !manager->priv->fading)
+ {
+ gtk_widget_show (GTK_WIDGET (window));
+ }
+}
+
+static void
+on_screen_monitors_changed (GdkScreen *screen,
+ GSManager *manager)
+{
+ GSList *l;
+ int n_monitors;
+ int n_windows;
+ int i;
+
+ n_monitors = gdk_screen_get_n_monitors (screen);
+ n_windows = g_slist_length (manager->priv->windows);
+
+ gs_debug ("Monitors changed for screen %d: num=%d",
+ gdk_screen_get_number (screen),
+ n_monitors);
+
+ if (n_monitors > n_windows)
+ {
+
+ /* Tear down unlock dialog in case we want to move it
+ * to a new monitor
+ */
+ l = manager->priv->windows;
+ while (l != NULL)
+ {
+ gs_window_cancel_unlock_request (GS_WINDOW (l->data));
+ l = l->next;
+ }
+
+ /* add more windows */
+ for (i = n_windows; i < n_monitors; i++)
+ {
+ gs_manager_create_window_for_monitor (manager, screen, i);
+ }
+
+ /* And put unlock dialog up where ever it's supposed to be
+ */
+ gs_manager_request_unlock (manager);
+ }
+ else
+ {
+
+ gdk_x11_grab_server ();
+
+ /* remove the extra windows */
+ l = manager->priv->windows;
+ while (l != NULL)
+ {
+ GdkScreen *this_screen;
+ int this_monitor;
+ GSList *next = l->next;
+
+ this_screen = gs_window_get_screen (GS_WINDOW (l->data));
+ this_monitor = gs_window_get_monitor (GS_WINDOW (l->data));
+ if (this_screen == screen && this_monitor >= n_monitors)
+ {
+ manager_maybe_stop_job_for_window (manager, GS_WINDOW (l->data));
+ g_hash_table_remove (manager->priv->jobs, l->data);
+ gs_window_destroy (GS_WINDOW (l->data));
+ manager->priv->windows = g_slist_delete_link (manager->priv->windows, l);
+ }
+ l = next;
+ }
+
+ /* make sure there is a lock dialog on a connected monitor,
+ * and that the keyboard is still properly grabbed after all
+ * the windows above got destroyed*/
+ if (n_windows > n_monitors)
+ {
+ gs_manager_request_unlock (manager);
+ }
+
+ gdk_flush ();
+ gdk_x11_ungrab_server ();
+ }
+}
+
+static void
+gs_manager_destroy_windows (GSManager *manager)
+{
+ GdkDisplay *display;
+ GSList *l;
+ int n_screens;
+ int i;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ if (manager->priv->windows == NULL)
+ {
+ return;
+ }
+
+ display = gdk_display_get_default ();
+
+ n_screens = gdk_display_get_n_screens (display);
+
+ for (i = 0; i < n_screens; i++)
+ {
+ g_signal_handlers_disconnect_by_func (gdk_display_get_screen (display, i),
+ on_screen_monitors_changed,
+ manager);
+ }
+
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_destroy (l->data);
+ }
+ g_slist_free (manager->priv->windows);
+ manager->priv->windows = NULL;
+}
+
+static void
+gs_manager_finalize (GObject *object)
+{
+ GSManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_MANAGER (object));
+
+ manager = GS_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ if (manager->priv->bg_notify_id != 0)
+ {
+ mateconf_client_remove_dir (manager->priv->client,
+ MATE_BG_KEY_DIR,
+ NULL);
+ mateconf_client_notify_remove (manager->priv->client,
+ manager->priv->bg_notify_id);
+ manager->priv->bg_notify_id = 0;
+ }
+ if (manager->priv->bg != NULL)
+ {
+ g_object_unref (manager->priv->bg);
+ }
+ if (manager->priv->client != NULL)
+ {
+ g_object_unref (manager->priv->client);
+ }
+
+ free_themes (manager);
+ g_free (manager->priv->logout_command);
+ g_free (manager->priv->keyboard_command);
+ g_free (manager->priv->status_message);
+
+ remove_unfade_idle (manager);
+ remove_timers (manager);
+
+ gs_grab_release (manager->priv->grab);
+
+ manager_stop_jobs (manager);
+
+ gs_manager_destroy_windows (manager);
+
+ manager->priv->active = FALSE;
+ manager->priv->activate_time = 0;
+ manager->priv->lock_enabled = FALSE;
+
+ g_object_unref (manager->priv->fade);
+ g_object_unref (manager->priv->grab);
+ g_object_unref (manager->priv->theme_manager);
+
+ G_OBJECT_CLASS (gs_manager_parent_class)->finalize (object);
+}
+
+static void
+gs_manager_create_windows_for_screen (GSManager *manager,
+ GdkScreen *screen)
+{
+ int n_monitors;
+ int i;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ g_object_ref (manager);
+ g_object_ref (screen);
+
+ n_monitors = gdk_screen_get_n_monitors (screen);
+
+ gs_debug ("Creating %d windows for screen %d", n_monitors, gdk_screen_get_number (screen));
+
+ for (i = 0; i < n_monitors; i++)
+ {
+ gs_manager_create_window_for_monitor (manager, screen, i);
+ }
+
+ g_object_unref (screen);
+ g_object_unref (manager);
+}
+
+static void
+gs_manager_create_windows (GSManager *manager)
+{
+ GdkDisplay *display;
+ int n_screens;
+ int i;
+
+ g_return_if_fail (manager != NULL);
+ g_return_if_fail (GS_IS_MANAGER (manager));
+
+ g_assert (manager->priv->windows == NULL);
+
+ display = gdk_display_get_default ();
+ n_screens = gdk_display_get_n_screens (display);
+
+ for (i = 0; i < n_screens; i++)
+ {
+ g_signal_connect (gdk_display_get_screen (display, i),
+ "monitors-changed",
+ G_CALLBACK (on_screen_monitors_changed),
+ manager);
+
+ gs_manager_create_windows_for_screen (manager, gdk_display_get_screen (display, i));
+ }
+}
+
+GSManager *
+gs_manager_new (void)
+{
+ GObject *manager;
+
+ manager = g_object_new (GS_TYPE_MANAGER, NULL);
+
+ return GS_MANAGER (manager);
+}
+
+static void
+show_windows (GSList *windows)
+{
+ GSList *l;
+
+ for (l = windows; l; l = l->next)
+ {
+ gtk_widget_show (GTK_WIDGET (l->data));
+ }
+}
+
+static void
+remove_job (GSJob *job)
+{
+ if (job == NULL)
+ {
+ return;
+ }
+
+ gs_job_stop (job);
+ g_object_unref (job);
+}
+
+static void
+fade_done_cb (GSFade *fade,
+ GSManager *manager)
+{
+ gs_debug ("fade completed, showing windows");
+ show_windows (manager->priv->windows);
+ manager->priv->fading = FALSE;
+}
+
+static gboolean
+gs_manager_activate (GSManager *manager)
+{
+ gboolean do_fade;
+ gboolean res;
+
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (manager->priv->active)
+ {
+ gs_debug ("Trying to activate manager when already active");
+ return FALSE;
+ }
+
+ res = gs_grab_grab_root (manager->priv->grab, FALSE);
+ if (! res)
+ {
+ return FALSE;
+ }
+
+ if (manager->priv->windows == NULL)
+ {
+ gs_manager_create_windows (GS_MANAGER (manager));
+ }
+
+ manager->priv->jobs = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify)remove_job);
+
+ manager->priv->active = TRUE;
+
+ /* fade to black and show windows */
+ do_fade = TRUE;
+ if (do_fade)
+ {
+ manager->priv->fading = TRUE;
+ gs_debug ("fading out");
+ gs_fade_async (manager->priv->fade,
+ FADE_TIMEOUT,
+ (GSFadeDoneFunc)fade_done_cb,
+ manager);
+
+ while (manager->priv->fading)
+ {
+ gtk_main_iteration ();
+ }
+ }
+ else
+ {
+ show_windows (manager->priv->windows);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gs_manager_deactivate (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (! manager->priv->active)
+ {
+ gs_debug ("Trying to deactivate a screensaver that is not active");
+ return FALSE;
+ }
+
+ remove_unfade_idle (manager);
+ gs_fade_reset (manager->priv->fade);
+ remove_timers (manager);
+
+ gs_grab_release (manager->priv->grab);
+
+ manager_stop_jobs (manager);
+
+ gs_manager_destroy_windows (manager);
+
+ /* reset state */
+ manager->priv->active = FALSE;
+ manager->priv->activate_time = 0;
+ manager->priv->lock_active = FALSE;
+ manager->priv->dialog_up = FALSE;
+ manager->priv->fading = FALSE;
+
+ return TRUE;
+}
+
+gboolean
+gs_manager_set_active (GSManager *manager,
+ gboolean active)
+{
+ gboolean res;
+
+ if (active)
+ {
+ res = gs_manager_activate (manager);
+ }
+ else
+ {
+ res = gs_manager_deactivate (manager);
+ }
+
+ return res;
+}
+
+gboolean
+gs_manager_get_active (GSManager *manager)
+{
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ return manager->priv->active;
+}
+
+gboolean
+gs_manager_request_unlock (GSManager *manager)
+{
+ GSWindow *window;
+
+ g_return_val_if_fail (manager != NULL, FALSE);
+ g_return_val_if_fail (GS_IS_MANAGER (manager), FALSE);
+
+ if (! manager->priv->active)
+ {
+ gs_debug ("Request unlock but manager is not active");
+ return FALSE;
+ }
+
+ if (manager->priv->dialog_up)
+ {
+ gs_debug ("Request unlock but dialog is already up");
+ return FALSE;
+ }
+
+ if (manager->priv->fading)
+ {
+ gs_debug ("Request unlock so finishing fade");
+ gs_fade_finish (manager->priv->fade);
+ }
+
+ if (manager->priv->windows == NULL)
+ {
+ gs_debug ("We don't have any windows!");
+ return FALSE;
+ }
+
+ /* Find the GSWindow that contains the pointer */
+ window = find_window_at_pointer (manager);
+ gs_window_request_unlock (window);
+
+ return TRUE;
+}
+
+void
+gs_manager_cancel_unlock_request (GSManager *manager)
+{
+ GSList *l;
+ for (l = manager->priv->windows; l; l = l->next)
+ {
+ gs_window_cancel_unlock_request (l->data);
+ }
+}
diff --git a/src/gs-manager.h b/src/gs-manager.h
new file mode 100644
index 0000000..61e64b6
--- /dev/null
+++ b/src/gs-manager.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_MANAGER_H
+#define __GS_MANAGER_H
+
+#include "gs-prefs.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_MANAGER (gs_manager_get_type ())
+#define GS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_MANAGER, GSManager))
+#define GS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_MANAGER, GSManagerClass))
+#define GS_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_MANAGER))
+#define GS_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_MANAGER))
+#define GS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_MANAGER, GSManagerClass))
+
+typedef struct GSManagerPrivate GSManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSManagerPrivate *priv;
+} GSManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* activated) (GSManager *manager);
+ void (* deactivated) (GSManager *manager);
+ void (* auth_request_begin) (GSManager *manager);
+ void (* auth_request_end) (GSManager *manager);
+
+} GSManagerClass;
+
+GType gs_manager_get_type (void);
+
+GSManager * gs_manager_new (void);
+
+gboolean gs_manager_set_active (GSManager *manager,
+ gboolean active);
+gboolean gs_manager_get_active (GSManager *manager);
+
+gboolean gs_manager_cycle (GSManager *manager);
+
+void gs_manager_get_lock_active (GSManager *manager,
+ gboolean *lock_active);
+void gs_manager_set_lock_active (GSManager *manager,
+ gboolean lock_active);
+void gs_manager_set_keyboard_enabled (GSManager *manager,
+ gboolean enabled);
+void gs_manager_set_keyboard_command (GSManager *manager,
+ const char *command);
+void gs_manager_set_status_message (GSManager *manager,
+ const char *message);
+void gs_manager_get_lock_enabled (GSManager *manager,
+ gboolean *lock_enabled);
+void gs_manager_set_lock_enabled (GSManager *manager,
+ gboolean lock_enabled);
+void gs_manager_set_lock_timeout (GSManager *manager,
+ glong lock_timeout);
+void gs_manager_set_logout_enabled (GSManager *manager,
+ gboolean logout_enabled);
+void gs_manager_set_user_switch_enabled (GSManager *manager,
+ gboolean user_switch_enabled);
+void gs_manager_set_logout_timeout (GSManager *manager,
+ glong logout_timeout);
+void gs_manager_set_logout_command (GSManager *manager,
+ const char *command);
+void gs_manager_set_throttled (GSManager *manager,
+ gboolean lock_enabled);
+void gs_manager_set_cycle_timeout (GSManager *manager,
+ glong cycle_timeout);
+void gs_manager_set_themes (GSManager *manager,
+ GSList *themes);
+void gs_manager_set_mode (GSManager *manager,
+ GSSaverMode mode);
+void gs_manager_show_message (GSManager *manager,
+ const char *summary,
+ const char *body,
+ const char *icon);
+gboolean gs_manager_request_unlock (GSManager *manager);
+void gs_manager_cancel_unlock_request (GSManager *manager);
+
+G_END_DECLS
+
+#endif /* __GS_MANAGER_H */
diff --git a/src/gs-manager.h.orig b/src/gs-manager.h.orig
new file mode 100644
index 0000000..6ef8db2
--- /dev/null
+++ b/src/gs-manager.h.orig
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_MANAGER_H
+#define __GS_MANAGER_H
+
+#include "gs-prefs.h"
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_MANAGER (gs_manager_get_type ())
+#define GS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_MANAGER, GSManager))
+#define GS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_MANAGER, GSManagerClass))
+#define GS_IS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_MANAGER))
+#define GS_IS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_MANAGER))
+#define GS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_MANAGER, GSManagerClass))
+
+typedef struct GSManagerPrivate GSManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSManagerPrivate *priv;
+} GSManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* activated) (GSManager *manager);
+ void (* deactivated) (GSManager *manager);
+ void (* auth_request_begin) (GSManager *manager);
+ void (* auth_request_end) (GSManager *manager);
+
+} GSManagerClass;
+
+GType gs_manager_get_type (void);
+
+GSManager * gs_manager_new (void);
+
+gboolean gs_manager_set_active (GSManager *manager,
+ gboolean active);
+gboolean gs_manager_get_active (GSManager *manager);
+
+gboolean gs_manager_cycle (GSManager *manager);
+
+void gs_manager_get_lock_active (GSManager *manager,
+ gboolean *lock_active);
+void gs_manager_set_lock_active (GSManager *manager,
+ gboolean lock_active);
+void gs_manager_set_keyboard_enabled (GSManager *manager,
+ gboolean enabled);
+void gs_manager_set_keyboard_command (GSManager *manager,
+ const char *command);
+void gs_manager_set_status_message (GSManager *manager,
+ const char *message);
+void gs_manager_get_lock_enabled (GSManager *manager,
+ gboolean *lock_enabled);
+void gs_manager_set_lock_enabled (GSManager *manager,
+ gboolean lock_enabled);
+void gs_manager_set_lock_timeout (GSManager *manager,
+ glong lock_timeout);
+void gs_manager_set_logout_enabled (GSManager *manager,
+ gboolean logout_enabled);
+void gs_manager_set_user_switch_enabled (GSManager *manager,
+ gboolean user_switch_enabled);
+void gs_manager_set_logout_timeout (GSManager *manager,
+ glong logout_timeout);
+void gs_manager_set_logout_command (GSManager *manager,
+ const char *command);
+void gs_manager_set_throttled (GSManager *manager,
+ gboolean lock_enabled);
+void gs_manager_set_cycle_timeout (GSManager *manager,
+ glong cycle_timeout);
+void gs_manager_set_themes (GSManager *manager,
+ GSList *themes);
+void gs_manager_set_mode (GSManager *manager,
+ GSSaverMode mode);
+void gs_manager_show_message (GSManager *manager,
+ const char *summary,
+ const char *body,
+ const char *icon);
+gboolean gs_manager_request_unlock (GSManager *manager);
+void gs_manager_cancel_unlock_request (GSManager *manager);
+
+G_END_DECLS
+
+#endif /* __GS_MANAGER_H */
diff --git a/src/gs-marshal.list b/src/gs-marshal.list
new file mode 100644
index 0000000..9a4d620
--- /dev/null
+++ b/src/gs-marshal.list
@@ -0,0 +1,4 @@
+BOOLEAN:VOID
+BOOLEAN:INT
+BOOLEAN:BOOLEAN
+VOID:STRING,STRING,STRING
diff --git a/src/gs-monitor.c b/src/gs-monitor.c
new file mode 100644
index 0000000..2703ba3
--- /dev/null
+++ b/src/gs-monitor.c
@@ -0,0 +1,528 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mate-screensaver.h"
+
+#include "gs-manager.h"
+#include "gs-watcher.h"
+#include "gs-fade.h"
+#include "gs-grab.h"
+
+#include "gs-listener-dbus.h"
+#include "gs-monitor.h"
+#include "gs-prefs.h"
+#include "gs-debug.h"
+
+static void gs_monitor_class_init (GSMonitorClass *klass);
+static void gs_monitor_init (GSMonitor *monitor);
+static void gs_monitor_finalize (GObject *object);
+
+#define GS_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_MONITOR, GSMonitorPrivate))
+
+struct GSMonitorPrivate
+{
+ GSWatcher *watcher;
+ GSListener *listener;
+ GSManager *manager;
+ GSPrefs *prefs;
+ GSFade *fade;
+ GSGrab *grab;
+ guint release_grab_id;
+};
+
+#define FADE_TIMEOUT 10000
+
+G_DEFINE_TYPE (GSMonitor, gs_monitor, G_TYPE_OBJECT)
+
+static void
+gs_monitor_class_init (GSMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_monitor_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSMonitorPrivate));
+}
+
+static void
+manager_activated_cb (GSManager *manager,
+ GSMonitor *monitor)
+{
+}
+
+static void
+manager_deactivated_cb (GSManager *manager,
+ GSMonitor *monitor)
+{
+ gs_listener_set_active (monitor->priv->listener, FALSE);
+}
+
+static gboolean
+watcher_idle_cb (GSWatcher *watcher,
+ gboolean is_idle,
+ GSMonitor *monitor)
+{
+ gboolean res;
+
+ gs_debug ("Idle signal detected: %d", is_idle);
+
+ res = gs_listener_set_session_idle (monitor->priv->listener, is_idle);
+
+ return res;
+}
+
+static gboolean
+release_grab_timeout (GSMonitor *monitor)
+{
+ gboolean manager_active;
+
+ manager_active = gs_manager_get_active (monitor->priv->manager);
+ if (! manager_active)
+ {
+ gs_grab_release (monitor->priv->grab);
+ }
+
+ monitor->priv->release_grab_id = 0;
+ return FALSE;
+}
+
+static gboolean
+watcher_idle_notice_cb (GSWatcher *watcher,
+ gboolean in_effect,
+ GSMonitor *monitor)
+{
+ gboolean activation_enabled;
+ gboolean inhibited;
+ gboolean handled;
+
+ gs_debug ("Idle notice signal detected: %d", in_effect);
+
+ /* only fade if screensaver can activate */
+ activation_enabled = gs_listener_get_activation_enabled (monitor->priv->listener);
+ inhibited = gs_listener_is_inhibited (monitor->priv->listener);
+
+ handled = FALSE;
+ if (in_effect)
+ {
+ if (activation_enabled && ! inhibited)
+ {
+ /* start slow fade */
+ if (gs_grab_grab_offscreen (monitor->priv->grab, FALSE))
+ {
+ gs_fade_async (monitor->priv->fade, FADE_TIMEOUT, NULL, NULL);
+ }
+ else
+ {
+ gs_debug ("Could not grab the keyboard so not performing idle warning fade-out");
+ }
+
+ handled = TRUE;
+ }
+ }
+ else
+ {
+ gboolean manager_active;
+
+ manager_active = gs_manager_get_active (monitor->priv->manager);
+ /* cancel the fade unless manager was activated */
+ if (! manager_active)
+ {
+ gs_debug ("manager not active, performing fade cancellation");
+ gs_fade_reset (monitor->priv->fade);
+
+ /* don't release the grab immediately to prevent typing passwords into windows */
+ if (monitor->priv->release_grab_id != 0)
+ {
+ g_source_remove (monitor->priv->release_grab_id);
+ }
+ monitor->priv->release_grab_id = g_timeout_add (1000, (GSourceFunc)release_grab_timeout, monitor);
+ }
+ else
+ {
+ gs_debug ("manager active, skipping fade cancellation");
+ }
+
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+static void
+gs_monitor_lock_screen (GSMonitor *monitor)
+{
+ gboolean res;
+ gboolean locked;
+
+ /* set lock flag before trying to activate screensaver
+ in case something tries to react to the ActiveChanged signal */
+
+ gs_manager_get_lock_active (monitor->priv->manager, &locked);
+ gs_manager_set_lock_active (monitor->priv->manager, TRUE);
+ res = gs_listener_set_active (monitor->priv->listener, TRUE);
+ if (! res)
+ {
+ /* If we've failed then restore lock status */
+ gs_manager_set_lock_active (monitor->priv->manager, locked);
+ gs_debug ("Unable to lock the screen");
+ }
+}
+
+static void
+gs_monitor_simulate_user_activity (GSMonitor *monitor)
+{
+ /* FIXME: reset the xsync timer? */
+
+ /* request that the manager unlock -
+ will pop up a dialog if necessary */
+ gs_manager_request_unlock (monitor->priv->manager);
+}
+
+static void
+listener_lock_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ if (! monitor->priv->prefs->lock_disabled)
+ {
+ gs_monitor_lock_screen (monitor);
+ }
+ else
+ {
+ gs_debug ("Locking disabled by the administrator");
+ }
+
+}
+
+static void
+listener_quit_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ gs_listener_set_active (monitor->priv->listener, FALSE);
+ mate_screensaver_quit ();
+}
+
+static void
+listener_cycle_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ gs_manager_cycle (monitor->priv->manager);
+}
+
+static void
+listener_show_message_cb (GSListener *listener,
+ const char *summary,
+ const char *body,
+ const char *icon,
+ GSMonitor *monitor)
+{
+ gs_manager_show_message (monitor->priv->manager,
+ summary,
+ body,
+ icon);
+}
+
+static gboolean
+listener_active_changed_cb (GSListener *listener,
+ gboolean active,
+ GSMonitor *monitor)
+{
+ gboolean res;
+ gboolean ret;
+ gboolean idle_watch_enabled;
+
+ res = gs_manager_set_active (monitor->priv->manager, active);
+ if (! res)
+ {
+ gs_debug ("Unable to set manager active: %d", active);
+ ret = FALSE;
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+
+ idle_watch_enabled = gs_watcher_get_enabled (monitor->priv->watcher);
+ if (ret && idle_watch_enabled)
+ {
+ res = gs_watcher_set_active (monitor->priv->watcher, !active);
+ if (! res)
+ {
+ gs_debug ("Unable to set the idle watcher active: %d", !active);
+ }
+ }
+
+ return ret;
+}
+
+static void
+listener_throttle_changed_cb (GSListener *listener,
+ gboolean throttled,
+ GSMonitor *monitor)
+{
+ gs_manager_set_throttled (monitor->priv->manager, throttled);
+}
+
+static void
+listener_simulate_user_activity_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ gs_monitor_simulate_user_activity (monitor);
+}
+
+static void
+_gs_monitor_update_from_prefs (GSMonitor *monitor,
+ GSPrefs *prefs)
+{
+ gboolean idle_detection_enabled;
+ gboolean idle_detection_active;
+ gboolean activate_watch;
+ gboolean manager_active;
+ gboolean lock_enabled;
+ gboolean user_switch_enabled;
+
+ lock_enabled = (monitor->priv->prefs->lock_enabled && !monitor->priv->prefs->lock_disabled);
+ user_switch_enabled = (monitor->priv->prefs->user_switch_enabled && !monitor->priv->prefs->user_switch_disabled);
+
+ gs_manager_set_lock_enabled (monitor->priv->manager, lock_enabled);
+ gs_manager_set_lock_timeout (monitor->priv->manager, monitor->priv->prefs->lock_timeout);
+ gs_manager_set_logout_enabled (monitor->priv->manager, monitor->priv->prefs->logout_enabled);
+ gs_manager_set_user_switch_enabled (monitor->priv->manager, user_switch_enabled);
+ gs_manager_set_keyboard_enabled (monitor->priv->manager, monitor->priv->prefs->keyboard_enabled);
+ gs_manager_set_logout_timeout (monitor->priv->manager, monitor->priv->prefs->logout_timeout);
+ gs_manager_set_logout_command (monitor->priv->manager, monitor->priv->prefs->logout_command);
+ gs_manager_set_keyboard_command (monitor->priv->manager, monitor->priv->prefs->keyboard_command);
+ gs_manager_set_cycle_timeout (monitor->priv->manager, monitor->priv->prefs->cycle);
+ gs_manager_set_mode (monitor->priv->manager, monitor->priv->prefs->mode);
+ gs_manager_set_themes (monitor->priv->manager, monitor->priv->prefs->themes);
+
+ /* enable activation when allowed */
+ gs_listener_set_activation_enabled (monitor->priv->listener,
+ monitor->priv->prefs->idle_activation_enabled);
+
+ /* idle detection always enabled */
+ idle_detection_enabled = TRUE;
+
+ gs_watcher_set_enabled (monitor->priv->watcher, idle_detection_enabled);
+
+ /* in the case where idle detection is reenabled we may need to
+ activate the watcher too */
+
+ manager_active = gs_manager_get_active (monitor->priv->manager);
+ idle_detection_active = gs_watcher_get_active (monitor->priv->watcher);
+ activate_watch = (! manager_active
+ && ! idle_detection_active
+ && idle_detection_enabled);
+ if (activate_watch)
+ {
+ gs_watcher_set_active (monitor->priv->watcher, TRUE);
+ }
+
+ if (monitor->priv->prefs->status_message_enabled)
+ {
+ char *text;
+ g_object_get (monitor->priv->watcher,
+ "status-message", &text,
+ NULL);
+ gs_manager_set_status_message (monitor->priv->manager, text);
+ g_free (text);
+ }
+ else
+ {
+ gs_manager_set_status_message (monitor->priv->manager, NULL);
+ }
+}
+
+static void
+disconnect_listener_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_lock_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_quit_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_cycle_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_active_changed_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_throttle_changed_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_simulate_user_activity_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_show_message_cb, monitor);
+}
+
+static void
+connect_listener_signals (GSMonitor *monitor)
+{
+ g_signal_connect (monitor->priv->listener, "lock",
+ G_CALLBACK (listener_lock_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "quit",
+ G_CALLBACK (listener_quit_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "cycle",
+ G_CALLBACK (listener_cycle_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "active-changed",
+ G_CALLBACK (listener_active_changed_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "throttle-changed",
+ G_CALLBACK (listener_throttle_changed_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "simulate-user-activity",
+ G_CALLBACK (listener_simulate_user_activity_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "show-message",
+ G_CALLBACK (listener_show_message_cb), monitor);
+}
+
+static void
+on_watcher_status_message_changed (GSWatcher *watcher,
+ GParamSpec *pspec,
+ GSMonitor *monitor)
+{
+ char *text;
+ g_object_get (watcher, "status-message", &text, NULL);
+ gs_manager_set_status_message (monitor->priv->manager, text);
+ g_free (text);
+}
+
+static void
+disconnect_watcher_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->watcher, watcher_idle_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->watcher, watcher_idle_notice_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->watcher, on_watcher_status_message_changed, monitor);
+}
+
+static void
+connect_watcher_signals (GSMonitor *monitor)
+{
+ g_signal_connect (monitor->priv->watcher, "idle-changed",
+ G_CALLBACK (watcher_idle_cb), monitor);
+ g_signal_connect (monitor->priv->watcher, "idle-notice-changed",
+ G_CALLBACK (watcher_idle_notice_cb), monitor);
+ g_signal_connect (monitor->priv->watcher, "notify::status-message",
+ G_CALLBACK (on_watcher_status_message_changed), monitor);
+
+}
+
+static void
+disconnect_manager_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->manager, manager_activated_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->manager, manager_deactivated_cb, monitor);
+}
+
+static void
+connect_manager_signals (GSMonitor *monitor)
+{
+ g_signal_connect (monitor->priv->manager, "activated",
+ G_CALLBACK (manager_activated_cb), monitor);
+ g_signal_connect (monitor->priv->manager, "deactivated",
+ G_CALLBACK (manager_deactivated_cb), monitor);
+}
+
+static void
+disconnect_prefs_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->prefs, _gs_monitor_update_from_prefs, monitor);
+}
+
+static void
+connect_prefs_signals (GSMonitor *monitor)
+{
+ g_signal_connect_swapped (monitor->priv->prefs, "changed",
+ G_CALLBACK (_gs_monitor_update_from_prefs), monitor);
+}
+
+static void
+gs_monitor_init (GSMonitor *monitor)
+{
+
+ monitor->priv = GS_MONITOR_GET_PRIVATE (monitor);
+
+ monitor->priv->prefs = gs_prefs_new ();
+ connect_prefs_signals (monitor);
+
+ monitor->priv->listener = gs_listener_new ();
+ connect_listener_signals (monitor);
+
+ monitor->priv->fade = gs_fade_new ();
+ monitor->priv->grab = gs_grab_new ();
+
+ monitor->priv->watcher = gs_watcher_new ();
+ connect_watcher_signals (monitor);
+
+ monitor->priv->manager = gs_manager_new ();
+ connect_manager_signals (monitor);
+
+ _gs_monitor_update_from_prefs (monitor, monitor->priv->prefs);
+}
+
+static void
+gs_monitor_finalize (GObject *object)
+{
+ GSMonitor *monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_MONITOR (object));
+
+ monitor = GS_MONITOR (object);
+
+ g_return_if_fail (monitor->priv != NULL);
+
+ disconnect_watcher_signals (monitor);
+ disconnect_listener_signals (monitor);
+ disconnect_manager_signals (monitor);
+ disconnect_prefs_signals (monitor);
+
+ g_object_unref (monitor->priv->fade);
+ g_object_unref (monitor->priv->grab);
+ g_object_unref (monitor->priv->watcher);
+ g_object_unref (monitor->priv->listener);
+ g_object_unref (monitor->priv->manager);
+ g_object_unref (monitor->priv->prefs);
+
+ G_OBJECT_CLASS (gs_monitor_parent_class)->finalize (object);
+}
+
+GSMonitor *
+gs_monitor_new (void)
+{
+ GSMonitor *monitor;
+
+ monitor = g_object_new (GS_TYPE_MONITOR, NULL);
+
+ return GS_MONITOR (monitor);
+}
+
+gboolean
+gs_monitor_start (GSMonitor *monitor,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_MONITOR (monitor), FALSE);
+
+ if (! gs_listener_acquire (monitor->priv->listener, error))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/gs-monitor.c.orig b/src/gs-monitor.c.orig
new file mode 100644
index 0000000..c8478a4
--- /dev/null
+++ b/src/gs-monitor.c.orig
@@ -0,0 +1,528 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mate-screensaver.h"
+
+#include "gs-manager.h"
+#include "gs-watcher.h"
+#include "gs-fade.h"
+#include "gs-grab.h"
+
+#include "gs-listener-dbus.h"
+#include "gs-monitor.h"
+#include "gs-prefs.h"
+#include "gs-debug.h"
+
+static void gs_monitor_class_init (GSMonitorClass *klass);
+static void gs_monitor_init (GSMonitor *monitor);
+static void gs_monitor_finalize (GObject *object);
+
+#define GS_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_MONITOR, GSMonitorPrivate))
+
+struct GSMonitorPrivate
+{
+ GSWatcher *watcher;
+ GSListener *listener;
+ GSManager *manager;
+ GSPrefs *prefs;
+ GSFade *fade;
+ GSGrab *grab;
+ guint release_grab_id;
+};
+
+#define FADE_TIMEOUT 10000
+
+G_DEFINE_TYPE (GSMonitor, gs_monitor, G_TYPE_OBJECT)
+
+static void
+gs_monitor_class_init (GSMonitorClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_monitor_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSMonitorPrivate));
+}
+
+static void
+manager_activated_cb (GSManager *manager,
+ GSMonitor *monitor)
+{
+}
+
+static void
+manager_deactivated_cb (GSManager *manager,
+ GSMonitor *monitor)
+{
+ gs_listener_set_active (monitor->priv->listener, FALSE);
+}
+
+static gboolean
+watcher_idle_cb (GSWatcher *watcher,
+ gboolean is_idle,
+ GSMonitor *monitor)
+{
+ gboolean res;
+
+ gs_debug ("Idle signal detected: %d", is_idle);
+
+ res = gs_listener_set_session_idle (monitor->priv->listener, is_idle);
+
+ return res;
+}
+
+static gboolean
+release_grab_timeout (GSMonitor *monitor)
+{
+ gboolean manager_active;
+
+ manager_active = gs_manager_get_active (monitor->priv->manager);
+ if (! manager_active)
+ {
+ gs_grab_release (monitor->priv->grab);
+ }
+
+ monitor->priv->release_grab_id = 0;
+ return FALSE;
+}
+
+static gboolean
+watcher_idle_notice_cb (GSWatcher *watcher,
+ gboolean in_effect,
+ GSMonitor *monitor)
+{
+ gboolean activation_enabled;
+ gboolean inhibited;
+ gboolean handled;
+
+ gs_debug ("Idle notice signal detected: %d", in_effect);
+
+ /* only fade if screensaver can activate */
+ activation_enabled = gs_listener_get_activation_enabled (monitor->priv->listener);
+ inhibited = gs_listener_is_inhibited (monitor->priv->listener);
+
+ handled = FALSE;
+ if (in_effect)
+ {
+ if (activation_enabled && ! inhibited)
+ {
+ /* start slow fade */
+ if (gs_grab_grab_offscreen (monitor->priv->grab, FALSE))
+ {
+ gs_fade_async (monitor->priv->fade, FADE_TIMEOUT, NULL, NULL);
+ }
+ else
+ {
+ gs_debug ("Could not grab the keyboard so not performing idle warning fade-out");
+ }
+
+ handled = TRUE;
+ }
+ }
+ else
+ {
+ gboolean manager_active;
+
+ manager_active = gs_manager_get_active (monitor->priv->manager);
+ /* cancel the fade unless manager was activated */
+ if (! manager_active)
+ {
+ gs_debug ("manager not active, performing fade cancellation");
+ gs_fade_reset (monitor->priv->fade);
+
+ /* don't release the grab immediately to prevent typing passwords into windows */
+ if (monitor->priv->release_grab_id != 0)
+ {
+ g_source_remove (monitor->priv->release_grab_id);
+ }
+ monitor->priv->release_grab_id = g_timeout_add (1000, (GSourceFunc)release_grab_timeout, monitor);
+ }
+ else
+ {
+ gs_debug ("manager active, skipping fade cancellation");
+ }
+
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+static void
+gs_monitor_lock_screen (GSMonitor *monitor)
+{
+ gboolean res;
+ gboolean locked;
+
+ /* set lock flag before trying to activate screensaver
+ in case something tries to react to the ActiveChanged signal */
+
+ gs_manager_get_lock_active (monitor->priv->manager, &locked);
+ gs_manager_set_lock_active (monitor->priv->manager, TRUE);
+ res = gs_listener_set_active (monitor->priv->listener, TRUE);
+ if (! res)
+ {
+ /* If we've failed then restore lock status */
+ gs_manager_set_lock_active (monitor->priv->manager, locked);
+ gs_debug ("Unable to lock the screen");
+ }
+}
+
+static void
+gs_monitor_simulate_user_activity (GSMonitor *monitor)
+{
+ /* FIXME: reset the xsync timer? */
+
+ /* request that the manager unlock -
+ will pop up a dialog if necessary */
+ gs_manager_request_unlock (monitor->priv->manager);
+}
+
+static void
+listener_lock_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ if (! monitor->priv->prefs->lock_disabled)
+ {
+ gs_monitor_lock_screen (monitor);
+ }
+ else
+ {
+ gs_debug ("Locking disabled by the administrator");
+ }
+
+}
+
+static void
+listener_quit_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ gs_listener_set_active (monitor->priv->listener, FALSE);
+ mate_screensaver_quit ();
+}
+
+static void
+listener_cycle_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ gs_manager_cycle (monitor->priv->manager);
+}
+
+static void
+listener_show_message_cb (GSListener *listener,
+ const char *summary,
+ const char *body,
+ const char *icon,
+ GSMonitor *monitor)
+{
+ gs_manager_show_message (monitor->priv->manager,
+ summary,
+ body,
+ icon);
+}
+
+static gboolean
+listener_active_changed_cb (GSListener *listener,
+ gboolean active,
+ GSMonitor *monitor)
+{
+ gboolean res;
+ gboolean ret;
+ gboolean idle_watch_enabled;
+
+ res = gs_manager_set_active (monitor->priv->manager, active);
+ if (! res)
+ {
+ gs_debug ("Unable to set manager active: %d", active);
+ ret = FALSE;
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+
+ idle_watch_enabled = gs_watcher_get_enabled (monitor->priv->watcher);
+ if (ret && idle_watch_enabled)
+ {
+ res = gs_watcher_set_active (monitor->priv->watcher, !active);
+ if (! res)
+ {
+ gs_debug ("Unable to set the idle watcher active: %d", !active);
+ }
+ }
+
+ return ret;
+}
+
+static void
+listener_throttle_changed_cb (GSListener *listener,
+ gboolean throttled,
+ GSMonitor *monitor)
+{
+ gs_manager_set_throttled (monitor->priv->manager, throttled);
+}
+
+static void
+listener_simulate_user_activity_cb (GSListener *listener,
+ GSMonitor *monitor)
+{
+ gs_monitor_simulate_user_activity (monitor);
+}
+
+static void
+_gs_monitor_update_from_prefs (GSMonitor *monitor,
+ GSPrefs *prefs)
+{
+ gboolean idle_detection_enabled;
+ gboolean idle_detection_active;
+ gboolean activate_watch;
+ gboolean manager_active;
+ gboolean lock_enabled;
+ gboolean user_switch_enabled;
+
+ lock_enabled = (monitor->priv->prefs->lock_enabled && !monitor->priv->prefs->lock_disabled);
+ user_switch_enabled = (monitor->priv->prefs->user_switch_enabled && !monitor->priv->prefs->user_switch_disabled);
+
+ gs_manager_set_lock_enabled (monitor->priv->manager, lock_enabled);
+ gs_manager_set_lock_timeout (monitor->priv->manager, monitor->priv->prefs->lock_timeout);
+ gs_manager_set_logout_enabled (monitor->priv->manager, monitor->priv->prefs->logout_enabled);
+ gs_manager_set_user_switch_enabled (monitor->priv->manager, user_switch_enabled);
+ gs_manager_set_keyboard_enabled (monitor->priv->manager, monitor->priv->prefs->keyboard_enabled);
+ gs_manager_set_logout_timeout (monitor->priv->manager, monitor->priv->prefs->logout_timeout);
+ gs_manager_set_logout_command (monitor->priv->manager, monitor->priv->prefs->logout_command);
+ gs_manager_set_keyboard_command (monitor->priv->manager, monitor->priv->prefs->keyboard_command);
+ gs_manager_set_cycle_timeout (monitor->priv->manager, monitor->priv->prefs->cycle);
+ gs_manager_set_mode (monitor->priv->manager, monitor->priv->prefs->mode);
+ gs_manager_set_themes (monitor->priv->manager, monitor->priv->prefs->themes);
+
+ /* enable activation when allowed */
+ gs_listener_set_activation_enabled (monitor->priv->listener,
+ monitor->priv->prefs->idle_activation_enabled);
+
+ /* idle detection always enabled */
+ idle_detection_enabled = TRUE;
+
+ gs_watcher_set_enabled (monitor->priv->watcher, idle_detection_enabled);
+
+ /* in the case where idle detection is reenabled we may need to
+ activate the watcher too */
+
+ manager_active = gs_manager_get_active (monitor->priv->manager);
+ idle_detection_active = gs_watcher_get_active (monitor->priv->watcher);
+ activate_watch = (! manager_active
+ && ! idle_detection_active
+ && idle_detection_enabled);
+ if (activate_watch)
+ {
+ gs_watcher_set_active (monitor->priv->watcher, TRUE);
+ }
+
+ if (monitor->priv->prefs->status_message_enabled)
+ {
+ char *text;
+ g_object_get (monitor->priv->watcher,
+ "status-message", &text,
+ NULL);
+ gs_manager_set_status_message (monitor->priv->manager, text);
+ g_free (text);
+ }
+ else
+ {
+ gs_manager_set_status_message (monitor->priv->manager, NULL);
+ }
+}
+
+static void
+disconnect_listener_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_lock_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_quit_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_cycle_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_active_changed_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_throttle_changed_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_simulate_user_activity_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->listener, listener_show_message_cb, monitor);
+}
+
+static void
+connect_listener_signals (GSMonitor *monitor)
+{
+ g_signal_connect (monitor->priv->listener, "lock",
+ G_CALLBACK (listener_lock_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "quit",
+ G_CALLBACK (listener_quit_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "cycle",
+ G_CALLBACK (listener_cycle_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "active-changed",
+ G_CALLBACK (listener_active_changed_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "throttle-changed",
+ G_CALLBACK (listener_throttle_changed_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "simulate-user-activity",
+ G_CALLBACK (listener_simulate_user_activity_cb), monitor);
+ g_signal_connect (monitor->priv->listener, "show-message",
+ G_CALLBACK (listener_show_message_cb), monitor);
+}
+
+static void
+on_watcher_status_message_changed (GSWatcher *watcher,
+ GParamSpec *pspec,
+ GSMonitor *monitor)
+{
+ char *text;
+ g_object_get (watcher, "status-message", &text, NULL);
+ gs_manager_set_status_message (monitor->priv->manager, text);
+ g_free (text);
+}
+
+static void
+disconnect_watcher_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->watcher, watcher_idle_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->watcher, watcher_idle_notice_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->watcher, on_watcher_status_message_changed, monitor);
+}
+
+static void
+connect_watcher_signals (GSMonitor *monitor)
+{
+ g_signal_connect (monitor->priv->watcher, "idle-changed",
+ G_CALLBACK (watcher_idle_cb), monitor);
+ g_signal_connect (monitor->priv->watcher, "idle-notice-changed",
+ G_CALLBACK (watcher_idle_notice_cb), monitor);
+ g_signal_connect (monitor->priv->watcher, "notify::status-message",
+ G_CALLBACK (on_watcher_status_message_changed), monitor);
+
+}
+
+static void
+disconnect_manager_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->manager, manager_activated_cb, monitor);
+ g_signal_handlers_disconnect_by_func (monitor->priv->manager, manager_deactivated_cb, monitor);
+}
+
+static void
+connect_manager_signals (GSMonitor *monitor)
+{
+ g_signal_connect (monitor->priv->manager, "activated",
+ G_CALLBACK (manager_activated_cb), monitor);
+ g_signal_connect (monitor->priv->manager, "deactivated",
+ G_CALLBACK (manager_deactivated_cb), monitor);
+}
+
+static void
+disconnect_prefs_signals (GSMonitor *monitor)
+{
+ g_signal_handlers_disconnect_by_func (monitor->priv->prefs, _gs_monitor_update_from_prefs, monitor);
+}
+
+static void
+connect_prefs_signals (GSMonitor *monitor)
+{
+ g_signal_connect_swapped (monitor->priv->prefs, "changed",
+ G_CALLBACK (_gs_monitor_update_from_prefs), monitor);
+}
+
+static void
+gs_monitor_init (GSMonitor *monitor)
+{
+
+ monitor->priv = GS_MONITOR_GET_PRIVATE (monitor);
+
+ monitor->priv->prefs = gs_prefs_new ();
+ connect_prefs_signals (monitor);
+
+ monitor->priv->listener = gs_listener_new ();
+ connect_listener_signals (monitor);
+
+ monitor->priv->fade = gs_fade_new ();
+ monitor->priv->grab = gs_grab_new ();
+
+ monitor->priv->watcher = gs_watcher_new ();
+ connect_watcher_signals (monitor);
+
+ monitor->priv->manager = gs_manager_new ();
+ connect_manager_signals (monitor);
+
+ _gs_monitor_update_from_prefs (monitor, monitor->priv->prefs);
+}
+
+static void
+gs_monitor_finalize (GObject *object)
+{
+ GSMonitor *monitor;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_MONITOR (object));
+
+ monitor = GS_MONITOR (object);
+
+ g_return_if_fail (monitor->priv != NULL);
+
+ disconnect_watcher_signals (monitor);
+ disconnect_listener_signals (monitor);
+ disconnect_manager_signals (monitor);
+ disconnect_prefs_signals (monitor);
+
+ g_object_unref (monitor->priv->fade);
+ g_object_unref (monitor->priv->grab);
+ g_object_unref (monitor->priv->watcher);
+ g_object_unref (monitor->priv->listener);
+ g_object_unref (monitor->priv->manager);
+ g_object_unref (monitor->priv->prefs);
+
+ G_OBJECT_CLASS (gs_monitor_parent_class)->finalize (object);
+}
+
+GSMonitor *
+gs_monitor_new (void)
+{
+ GSMonitor *monitor;
+
+ monitor = g_object_new (GS_TYPE_MONITOR, NULL);
+
+ return GS_MONITOR (monitor);
+}
+
+gboolean
+gs_monitor_start (GSMonitor *monitor,
+ GError **error)
+{
+ g_return_val_if_fail (GS_IS_MONITOR (monitor), FALSE);
+
+ if (! gs_listener_acquire (monitor->priv->listener, error))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/src/gs-monitor.h b/src/gs-monitor.h
new file mode 100644
index 0000000..6acc18c
--- /dev/null
+++ b/src/gs-monitor.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_MONITOR_H
+#define __GS_MONITOR_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_MONITOR (gs_monitor_get_type ())
+#define GS_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_MONITOR, GSMonitor))
+#define GS_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_MONITOR, GSMonitorClass))
+#define GS_IS_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_MONITOR))
+#define GS_IS_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_MONITOR))
+#define GS_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_MONITOR, GSMonitorClass))
+
+typedef struct GSMonitorPrivate GSMonitorPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSMonitorPrivate *priv;
+} GSMonitor;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GSMonitorClass;
+
+GType gs_monitor_get_type (void);
+
+GSMonitor * gs_monitor_new (void);
+gboolean gs_monitor_start (GSMonitor *monitor,
+ GError **error);
+void gs_monitor_set_lock_enabled (GSMonitor *monitor,
+ gboolean lock_enabled);
+G_END_DECLS
+
+#endif /* __GS_MONITOR_H */
diff --git a/src/gs-monitor.h.orig b/src/gs-monitor.h.orig
new file mode 100644
index 0000000..8ab3b0d
--- /dev/null
+++ b/src/gs-monitor.h.orig
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_MONITOR_H
+#define __GS_MONITOR_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_MONITOR (gs_monitor_get_type ())
+#define GS_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_MONITOR, GSMonitor))
+#define GS_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_MONITOR, GSMonitorClass))
+#define GS_IS_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_MONITOR))
+#define GS_IS_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_MONITOR))
+#define GS_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_MONITOR, GSMonitorClass))
+
+typedef struct GSMonitorPrivate GSMonitorPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSMonitorPrivate *priv;
+} GSMonitor;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GSMonitorClass;
+
+GType gs_monitor_get_type (void);
+
+GSMonitor * gs_monitor_new (void);
+gboolean gs_monitor_start (GSMonitor *monitor,
+ GError **error);
+void gs_monitor_set_lock_enabled (GSMonitor *monitor,
+ gboolean lock_enabled);
+G_END_DECLS
+
+#endif /* __GS_MONITOR_H */
diff --git a/src/gs-prefs.c b/src/gs-prefs.c
new file mode 100644
index 0000000..0f9d14e
--- /dev/null
+++ b/src/gs-prefs.c
@@ -0,0 +1,995 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <mateconf/mateconf-client.h>
+
+#include "gs-prefs.h"
+
+static void gs_prefs_class_init (GSPrefsClass *klass);
+static void gs_prefs_init (GSPrefs *prefs);
+static void gs_prefs_finalize (GObject *object);
+
+#define MATE_LOCKDOWN_DIR "/desktop/mate/lockdown"
+#define KEY_LOCK_DISABLE MATE_LOCKDOWN_DIR "/disable_lock_screen"
+#define KEY_USER_SWITCH_DISABLE MATE_LOCKDOWN_DIR "/disable_user_switching"
+
+#define KEY_DIR "/apps/mate-screensaver"
+#define MATE_SESSION_DIR "/desktop/mate/session"
+#define KEY_IDLE_ACTIVATION_ENABLED KEY_DIR "/idle_activation_enabled"
+#define KEY_LOCK_ENABLED KEY_DIR "/lock_enabled"
+#define KEY_MODE KEY_DIR "/mode"
+#define KEY_ACTIVATE_DELAY MATE_SESSION_DIR "/idle_delay"
+#define KEY_POWER_DELAY KEY_DIR "/power_management_delay"
+#define KEY_LOCK_DELAY KEY_DIR "/lock_delay"
+#define KEY_CYCLE_DELAY KEY_DIR "/cycle_delay"
+#define KEY_THEMES KEY_DIR "/themes"
+#define KEY_USER_SWITCH_ENABLED KEY_DIR "/user_switch_enabled"
+#define KEY_LOGOUT_ENABLED KEY_DIR "/logout_enabled"
+#define KEY_LOGOUT_DELAY KEY_DIR "/logout_delay"
+#define KEY_LOGOUT_COMMAND KEY_DIR "/logout_command"
+#define KEY_KEYBOARD_ENABLED KEY_DIR "/embedded_keyboard_enabled"
+#define KEY_KEYBOARD_COMMAND KEY_DIR "/embedded_keyboard_command"
+#define KEY_STATUS_MESSAGE_ENABLED KEY_DIR "/status_message_enabled"
+
+#define GS_PREFS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_PREFS, GSPrefsPrivate))
+
+struct GSPrefsPrivate
+{
+ MateConfClient *mateconf_client;
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0
+};
+
+static MateConfEnumStringPair mode_enum_map [] =
+{
+ { GS_MODE_BLANK_ONLY, "blank-only" },
+ { GS_MODE_RANDOM, "random" },
+ { GS_MODE_SINGLE, "single" },
+ { 0, NULL }
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSPrefs, gs_prefs, G_TYPE_OBJECT)
+
+static void
+gs_prefs_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
+gs_prefs_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_prefs_class_init (GSPrefsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_prefs_finalize;
+ object_class->get_property = gs_prefs_get_property;
+ object_class->set_property = gs_prefs_set_property;
+
+
+ signals [CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSPrefsClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_type_class_add_private (klass, sizeof (GSPrefsPrivate));
+
+}
+
+static void
+_gs_prefs_set_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 1)
+ value = 10;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->timeout = value * 60000;
+}
+
+static void
+_gs_prefs_set_power_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 1)
+ value = 60;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ /* this value is in seconds - others are in minutes */
+ prefs->power_timeout = value * 1000;
+}
+
+static void
+_gs_prefs_set_lock_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 0)
+ value = 0;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->lock_timeout = value * 60000;
+}
+
+static void
+_gs_prefs_set_cycle_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 1)
+ value = 1;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->cycle = value * 60000;
+}
+
+static void
+_gs_prefs_set_mode (GSPrefs *prefs,
+ const char *value)
+{
+ int mode;
+
+ if (value && mateconf_string_to_enum (mode_enum_map, value, &mode))
+ prefs->mode = mode;
+ else
+ prefs->mode = GS_MODE_BLANK_ONLY;
+}
+
+static void
+_gs_prefs_set_themes (GSPrefs *prefs,
+ GSList *list)
+{
+ if (prefs->themes)
+ {
+ g_slist_foreach (prefs->themes, (GFunc)g_free, NULL);
+ g_slist_free (prefs->themes);
+ }
+
+ /* take ownership of the list */
+ prefs->themes = list;
+}
+
+static void
+_gs_prefs_set_idle_activation_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->idle_activation_enabled = value;
+}
+
+static void
+_gs_prefs_set_lock_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->lock_enabled = value;
+}
+
+static void
+_gs_prefs_set_lock_disabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->lock_disabled = value;
+}
+
+static void
+_gs_prefs_set_user_switch_disabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->user_switch_disabled = value;
+}
+
+static void
+_gs_prefs_set_keyboard_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->keyboard_enabled = value;
+}
+
+static void
+_gs_prefs_set_keyboard_command (GSPrefs *prefs,
+ const char *value)
+{
+ g_free (prefs->keyboard_command);
+ prefs->keyboard_command = NULL;
+
+ if (value)
+ {
+ /* FIXME: check command */
+
+ prefs->keyboard_command = g_strdup (value);
+ }
+}
+
+static void
+_gs_prefs_set_status_message_enabled (GSPrefs *prefs,
+ gboolean enabled)
+{
+ prefs->status_message_enabled = enabled;
+}
+
+static void
+_gs_prefs_set_logout_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->logout_enabled = value;
+}
+
+static void
+_gs_prefs_set_logout_command (GSPrefs *prefs,
+ const char *value)
+{
+ g_free (prefs->logout_command);
+ prefs->logout_command = NULL;
+
+ if (value)
+ {
+ /* FIXME: check command */
+
+ prefs->logout_command = g_strdup (value);
+ }
+}
+
+static void
+_gs_prefs_set_logout_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 0)
+ value = 0;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->logout_timeout = value * 60000;
+}
+
+static void
+_gs_prefs_set_user_switch_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->user_switch_enabled = value;
+}
+
+static void
+key_error_and_free (const char *key,
+ GError *error)
+{
+ g_warning ("Error retrieving configuration key '%s': %s", key, error->message);
+ g_error_free (error);
+ error = NULL;
+}
+
+static void
+gs_prefs_load_from_mateconf (GSPrefs *prefs)
+{
+ glong value;
+ gboolean bvalue;
+ char *string;
+ GSList *list;
+ GError *error;
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_IDLE_ACTIVATION_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_idle_activation_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_IDLE_ACTIVATION_ENABLED, error);
+ }
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_LOCK_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_lock_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOCK_ENABLED, error);
+ }
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_LOCK_DISABLE, &error);
+ if (! error)
+ {
+ _gs_prefs_set_lock_disabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOCK_DISABLE, error);
+ }
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_USER_SWITCH_DISABLE, &error);
+ if (! error)
+ {
+ _gs_prefs_set_user_switch_disabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_USER_SWITCH_DISABLE, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_ACTIVATE_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_ACTIVATE_DELAY, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_POWER_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_power_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_POWER_DELAY, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_LOCK_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_lock_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOCK_DELAY, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_CYCLE_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_cycle_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_CYCLE_DELAY, error);
+ }
+
+ error = NULL;
+ string = mateconf_client_get_string (prefs->priv->mateconf_client, KEY_MODE, &error);
+ if (! error)
+ {
+ _gs_prefs_set_mode (prefs, string);
+ }
+ else
+ {
+ key_error_and_free (KEY_MODE, error);
+ }
+ g_free (string);
+
+ error = NULL;
+ list = mateconf_client_get_list (prefs->priv->mateconf_client,
+ KEY_THEMES,
+ MATECONF_VALUE_STRING,
+ &error);
+ if (! error)
+ {
+ _gs_prefs_set_themes (prefs, list);
+ }
+ else
+ {
+ key_error_and_free (KEY_THEMES, error);
+ }
+
+ /* Embedded keyboard options */
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_KEYBOARD_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_keyboard_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_KEYBOARD_ENABLED, error);
+ }
+
+ error = NULL;
+ string = mateconf_client_get_string (prefs->priv->mateconf_client, KEY_KEYBOARD_COMMAND, &error);
+ if (! error)
+ {
+ _gs_prefs_set_keyboard_command (prefs, string);
+ }
+ else
+ {
+ key_error_and_free (KEY_KEYBOARD_COMMAND, error);
+ }
+ g_free (string);
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_STATUS_MESSAGE_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_status_message_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_STATUS_MESSAGE_ENABLED, error);
+ }
+
+ /* Logout options */
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_LOGOUT_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_logout_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOGOUT_ENABLED, error);
+ }
+
+ error = NULL;
+ string = mateconf_client_get_string (prefs->priv->mateconf_client, KEY_LOGOUT_COMMAND, &error);
+ if (! error)
+ {
+ _gs_prefs_set_logout_command (prefs, string);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOGOUT_COMMAND, error);
+ }
+ g_free (string);
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_LOGOUT_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_logout_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOGOUT_DELAY, error);
+ }
+
+ /* User switching options */
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_USER_SWITCH_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_user_switch_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_USER_SWITCH_ENABLED, error);
+ }
+}
+
+static void
+invalid_type_warning (const char *type)
+{
+ g_warning ("Error retrieving configuration key '%s': Invalid type",
+ type);
+}
+
+static void
+key_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ GSPrefs *prefs)
+{
+ gboolean changed = FALSE;
+ const char *key;
+ MateConfValue *value;
+
+ key = mateconf_entry_get_key (entry);
+
+ if (! g_str_has_prefix (key, KEY_DIR) && ! g_str_has_prefix (key, MATE_LOCKDOWN_DIR))
+ return;
+
+ value = mateconf_entry_get_value (entry);
+
+ if (strcmp (key, KEY_MODE) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_STRING)
+ {
+ const char *str;
+
+ str = mateconf_value_get_string (value);
+ _gs_prefs_set_mode (prefs, str);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_THEMES) == 0)
+ {
+ GSList *list = NULL;
+
+ if (value == NULL
+ || value->type != MATECONF_VALUE_LIST)
+ {
+ return;
+ }
+
+ list = mateconf_value_get_list (value);
+
+ if (list
+ && mateconf_value_get_list_type (value) == MATECONF_VALUE_STRING)
+ {
+ GSList *l;
+ GSList *new_list;
+
+ changed = TRUE;
+
+ new_list = NULL;
+ for (l = list; l; l = l->next)
+ {
+ char *s;
+
+ s = mateconf_value_to_string (l->data);
+
+ new_list = g_slist_prepend (new_list, g_strdup (s));
+
+ g_free (s);
+ }
+
+ new_list = g_slist_reverse (new_list);
+
+ _gs_prefs_set_themes (prefs, new_list);
+
+ }
+
+ }
+ else if (strcmp (key, KEY_ACTIVATE_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_POWER_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_power_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOCK_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_lock_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_IDLE_ACTIVATION_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_idle_activation_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOCK_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_lock_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOCK_DISABLE) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_lock_disabled (prefs, disabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_USER_SWITCH_DISABLE) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_user_switch_disabled (prefs, disabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_CYCLE_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_cycle_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_KEYBOARD_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_keyboard_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_KEYBOARD_COMMAND) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_STRING)
+ {
+ const char *command;
+
+ command = mateconf_value_get_string (value);
+ _gs_prefs_set_keyboard_command (prefs, command);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_STATUS_MESSAGE_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_status_message_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOGOUT_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_logout_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOGOUT_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_logout_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOGOUT_COMMAND) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_STRING)
+ {
+ const char *command;
+
+ command = mateconf_value_get_string (value);
+ _gs_prefs_set_logout_command (prefs, command);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_USER_SWITCH_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_user_switch_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else
+ {
+ g_warning ("Config key not handled: %s", key);
+ }
+
+ if (changed && prefs)
+ {
+ g_signal_emit (prefs, signals [CHANGED], 0);
+ }
+}
+
+static void
+gs_prefs_init (GSPrefs *prefs)
+{
+ prefs->priv = GS_PREFS_GET_PRIVATE (prefs);
+
+ prefs->priv->mateconf_client = mateconf_client_get_default ();
+
+ prefs->idle_activation_enabled = TRUE;
+ prefs->lock_enabled = TRUE;
+ prefs->lock_disabled = FALSE;
+ prefs->logout_enabled = FALSE;
+ prefs->user_switch_enabled = FALSE;
+
+ prefs->timeout = 600000;
+ prefs->power_timeout = 60000;
+ prefs->lock_timeout = 0;
+ prefs->logout_timeout = 14400000;
+ prefs->cycle = 600000;
+
+ prefs->mode = GS_MODE_SINGLE;
+
+ /* MateConf setup */
+ mateconf_client_add_dir (prefs->priv->mateconf_client,
+ KEY_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE, NULL);
+ mateconf_client_add_dir (prefs->priv->mateconf_client,
+ MATE_LOCKDOWN_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE, NULL);
+ mateconf_client_add_dir (prefs->priv->mateconf_client,
+ MATE_SESSION_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE, NULL);
+
+
+ mateconf_client_notify_add (prefs->priv->mateconf_client,
+ KEY_DIR,
+ (MateConfClientNotifyFunc)key_changed_cb,
+ prefs,
+ NULL, NULL);
+ mateconf_client_notify_add (prefs->priv->mateconf_client,
+ MATE_LOCKDOWN_DIR,
+ (MateConfClientNotifyFunc)key_changed_cb,
+ prefs,
+ NULL, NULL);
+
+ gs_prefs_load_from_mateconf (prefs);
+}
+
+static void
+gs_prefs_finalize (GObject *object)
+{
+ GSPrefs *prefs;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_PREFS (object));
+
+ prefs = GS_PREFS (object);
+
+ g_return_if_fail (prefs->priv != NULL);
+
+ if (prefs->priv->mateconf_client)
+ {
+ g_object_unref (prefs->priv->mateconf_client);
+ prefs->priv->mateconf_client = NULL;
+ }
+
+ if (prefs->themes)
+ {
+ g_slist_foreach (prefs->themes, (GFunc)g_free, NULL);
+ g_slist_free (prefs->themes);
+ }
+
+ g_free (prefs->logout_command);
+ g_free (prefs->keyboard_command);
+
+ G_OBJECT_CLASS (gs_prefs_parent_class)->finalize (object);
+}
+
+GSPrefs *
+gs_prefs_new (void)
+{
+ GObject *prefs;
+
+ prefs = g_object_new (GS_TYPE_PREFS, NULL);
+
+ return GS_PREFS (prefs);
+}
diff --git a/src/gs-prefs.c.orig b/src/gs-prefs.c.orig
new file mode 100644
index 0000000..2aaf2d3
--- /dev/null
+++ b/src/gs-prefs.c.orig
@@ -0,0 +1,995 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <mateconf/mateconf-client.h>
+
+#include "gs-prefs.h"
+
+static void gs_prefs_class_init (GSPrefsClass *klass);
+static void gs_prefs_init (GSPrefs *prefs);
+static void gs_prefs_finalize (GObject *object);
+
+#define MATE_LOCKDOWN_DIR "/desktop/mate/lockdown"
+#define KEY_LOCK_DISABLE MATE_LOCKDOWN_DIR "/disable_lock_screen"
+#define KEY_USER_SWITCH_DISABLE MATE_LOCKDOWN_DIR "/disable_user_switching"
+
+#define KEY_DIR "/apps/mate-screensaver"
+#define MATE_SESSION_DIR "/desktop/mate/session"
+#define KEY_IDLE_ACTIVATION_ENABLED KEY_DIR "/idle_activation_enabled"
+#define KEY_LOCK_ENABLED KEY_DIR "/lock_enabled"
+#define KEY_MODE KEY_DIR "/mode"
+#define KEY_ACTIVATE_DELAY MATE_SESSION_DIR "/idle_delay"
+#define KEY_POWER_DELAY KEY_DIR "/power_management_delay"
+#define KEY_LOCK_DELAY KEY_DIR "/lock_delay"
+#define KEY_CYCLE_DELAY KEY_DIR "/cycle_delay"
+#define KEY_THEMES KEY_DIR "/themes"
+#define KEY_USER_SWITCH_ENABLED KEY_DIR "/user_switch_enabled"
+#define KEY_LOGOUT_ENABLED KEY_DIR "/logout_enabled"
+#define KEY_LOGOUT_DELAY KEY_DIR "/logout_delay"
+#define KEY_LOGOUT_COMMAND KEY_DIR "/logout_command"
+#define KEY_KEYBOARD_ENABLED KEY_DIR "/embedded_keyboard_enabled"
+#define KEY_KEYBOARD_COMMAND KEY_DIR "/embedded_keyboard_command"
+#define KEY_STATUS_MESSAGE_ENABLED KEY_DIR "/status_message_enabled"
+
+#define GS_PREFS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_PREFS, GSPrefsPrivate))
+
+struct GSPrefsPrivate
+{
+ MateConfClient *mateconf_client;
+};
+
+enum
+{
+ CHANGED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0
+};
+
+static MateConfEnumStringPair mode_enum_map [] =
+{
+ { GS_MODE_BLANK_ONLY, "blank-only" },
+ { GS_MODE_RANDOM, "random" },
+ { GS_MODE_SINGLE, "single" },
+ { 0, NULL }
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSPrefs, gs_prefs, G_TYPE_OBJECT)
+
+static void
+gs_prefs_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
+gs_prefs_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_prefs_class_init (GSPrefsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_prefs_finalize;
+ object_class->get_property = gs_prefs_get_property;
+ object_class->set_property = gs_prefs_set_property;
+
+
+ signals [CHANGED] =
+ g_signal_new ("changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSPrefsClass, changed),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_type_class_add_private (klass, sizeof (GSPrefsPrivate));
+
+}
+
+static void
+_gs_prefs_set_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 1)
+ value = 10;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->timeout = value * 60000;
+}
+
+static void
+_gs_prefs_set_power_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 1)
+ value = 60;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ /* this value is in seconds - others are in minutes */
+ prefs->power_timeout = value * 1000;
+}
+
+static void
+_gs_prefs_set_lock_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 0)
+ value = 0;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->lock_timeout = value * 60000;
+}
+
+static void
+_gs_prefs_set_cycle_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 1)
+ value = 1;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->cycle = value * 60000;
+}
+
+static void
+_gs_prefs_set_mode (GSPrefs *prefs,
+ const char *value)
+{
+ int mode;
+
+ if (value && mateconf_string_to_enum (mode_enum_map, value, &mode))
+ prefs->mode = mode;
+ else
+ prefs->mode = GS_MODE_BLANK_ONLY;
+}
+
+static void
+_gs_prefs_set_themes (GSPrefs *prefs,
+ GSList *list)
+{
+ if (prefs->themes)
+ {
+ g_slist_foreach (prefs->themes, (GFunc)g_free, NULL);
+ g_slist_free (prefs->themes);
+ }
+
+ /* take ownership of the list */
+ prefs->themes = list;
+}
+
+static void
+_gs_prefs_set_idle_activation_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->idle_activation_enabled = value;
+}
+
+static void
+_gs_prefs_set_lock_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->lock_enabled = value;
+}
+
+static void
+_gs_prefs_set_lock_disabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->lock_disabled = value;
+}
+
+static void
+_gs_prefs_set_user_switch_disabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->user_switch_disabled = value;
+}
+
+static void
+_gs_prefs_set_keyboard_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->keyboard_enabled = value;
+}
+
+static void
+_gs_prefs_set_keyboard_command (GSPrefs *prefs,
+ const char *value)
+{
+ g_free (prefs->keyboard_command);
+ prefs->keyboard_command = NULL;
+
+ if (value)
+ {
+ /* FIXME: check command */
+
+ prefs->keyboard_command = g_strdup (value);
+ }
+}
+
+static void
+_gs_prefs_set_status_message_enabled (GSPrefs *prefs,
+ gboolean enabled)
+{
+ prefs->status_message_enabled = enabled;
+}
+
+static void
+_gs_prefs_set_logout_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->logout_enabled = value;
+}
+
+static void
+_gs_prefs_set_logout_command (GSPrefs *prefs,
+ const char *value)
+{
+ g_free (prefs->logout_command);
+ prefs->logout_command = NULL;
+
+ if (value)
+ {
+ /* FIXME: check command */
+
+ prefs->logout_command = g_strdup (value);
+ }
+}
+
+static void
+_gs_prefs_set_logout_timeout (GSPrefs *prefs,
+ int value)
+{
+ if (value < 0)
+ value = 0;
+
+ /* pick a reasonable large number for the
+ upper bound */
+ if (value > 480)
+ value = 480;
+
+ prefs->logout_timeout = value * 60000;
+}
+
+static void
+_gs_prefs_set_user_switch_enabled (GSPrefs *prefs,
+ gboolean value)
+{
+ prefs->user_switch_enabled = value;
+}
+
+static void
+key_error_and_free (const char *key,
+ GError *error)
+{
+ g_warning ("Error retrieving configuration key '%s': %s", key, error->message);
+ g_error_free (error);
+ error = NULL;
+}
+
+static void
+gs_prefs_load_from_mateconf (GSPrefs *prefs)
+{
+ glong value;
+ gboolean bvalue;
+ char *string;
+ GSList *list;
+ GError *error;
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_IDLE_ACTIVATION_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_idle_activation_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_IDLE_ACTIVATION_ENABLED, error);
+ }
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_LOCK_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_lock_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOCK_ENABLED, error);
+ }
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_LOCK_DISABLE, &error);
+ if (! error)
+ {
+ _gs_prefs_set_lock_disabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOCK_DISABLE, error);
+ }
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_USER_SWITCH_DISABLE, &error);
+ if (! error)
+ {
+ _gs_prefs_set_user_switch_disabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_USER_SWITCH_DISABLE, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_ACTIVATE_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_ACTIVATE_DELAY, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_POWER_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_power_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_POWER_DELAY, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_LOCK_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_lock_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOCK_DELAY, error);
+ }
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_CYCLE_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_cycle_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_CYCLE_DELAY, error);
+ }
+
+ error = NULL;
+ string = mateconf_client_get_string (prefs->priv->mateconf_client, KEY_MODE, &error);
+ if (! error)
+ {
+ _gs_prefs_set_mode (prefs, string);
+ }
+ else
+ {
+ key_error_and_free (KEY_MODE, error);
+ }
+ g_free (string);
+
+ error = NULL;
+ list = mateconf_client_get_list (prefs->priv->mateconf_client,
+ KEY_THEMES,
+ MATECONF_VALUE_STRING,
+ &error);
+ if (! error)
+ {
+ _gs_prefs_set_themes (prefs, list);
+ }
+ else
+ {
+ key_error_and_free (KEY_THEMES, error);
+ }
+
+ /* Embedded keyboard options */
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_KEYBOARD_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_keyboard_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_KEYBOARD_ENABLED, error);
+ }
+
+ error = NULL;
+ string = mateconf_client_get_string (prefs->priv->mateconf_client, KEY_KEYBOARD_COMMAND, &error);
+ if (! error)
+ {
+ _gs_prefs_set_keyboard_command (prefs, string);
+ }
+ else
+ {
+ key_error_and_free (KEY_KEYBOARD_COMMAND, error);
+ }
+ g_free (string);
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_STATUS_MESSAGE_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_status_message_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_STATUS_MESSAGE_ENABLED, error);
+ }
+
+ /* Logout options */
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_LOGOUT_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_logout_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOGOUT_ENABLED, error);
+ }
+
+ error = NULL;
+ string = mateconf_client_get_string (prefs->priv->mateconf_client, KEY_LOGOUT_COMMAND, &error);
+ if (! error)
+ {
+ _gs_prefs_set_logout_command (prefs, string);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOGOUT_COMMAND, error);
+ }
+ g_free (string);
+
+ error = NULL;
+ value = mateconf_client_get_int (prefs->priv->mateconf_client, KEY_LOGOUT_DELAY, &error);
+ if (! error)
+ {
+ _gs_prefs_set_logout_timeout (prefs, value);
+ }
+ else
+ {
+ key_error_and_free (KEY_LOGOUT_DELAY, error);
+ }
+
+ /* User switching options */
+
+ error = NULL;
+ bvalue = mateconf_client_get_bool (prefs->priv->mateconf_client, KEY_USER_SWITCH_ENABLED, &error);
+ if (! error)
+ {
+ _gs_prefs_set_user_switch_enabled (prefs, bvalue);
+ }
+ else
+ {
+ key_error_and_free (KEY_USER_SWITCH_ENABLED, error);
+ }
+}
+
+static void
+invalid_type_warning (const char *type)
+{
+ g_warning ("Error retrieving configuration key '%s': Invalid type",
+ type);
+}
+
+static void
+key_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ GSPrefs *prefs)
+{
+ gboolean changed = FALSE;
+ const char *key;
+ MateConfValue *value;
+
+ key = mateconf_entry_get_key (entry);
+
+ if (! g_str_has_prefix (key, KEY_DIR) && ! g_str_has_prefix (key, MATE_LOCKDOWN_DIR))
+ return;
+
+ value = mateconf_entry_get_value (entry);
+
+ if (strcmp (key, KEY_MODE) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_STRING)
+ {
+ const char *str;
+
+ str = mateconf_value_get_string (value);
+ _gs_prefs_set_mode (prefs, str);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_THEMES) == 0)
+ {
+ GSList *list = NULL;
+
+ if (value == NULL
+ || value->type != MATECONF_VALUE_LIST)
+ {
+ return;
+ }
+
+ list = mateconf_value_get_list (value);
+
+ if (list
+ && mateconf_value_get_list_type (value) == MATECONF_VALUE_STRING)
+ {
+ GSList *l;
+ GSList *new_list;
+
+ changed = TRUE;
+
+ new_list = NULL;
+ for (l = list; l; l = l->next)
+ {
+ char *s;
+
+ s = mateconf_value_to_string (l->data);
+
+ new_list = g_slist_prepend (new_list, g_strdup (s));
+
+ g_free (s);
+ }
+
+ new_list = g_slist_reverse (new_list);
+
+ _gs_prefs_set_themes (prefs, new_list);
+
+ }
+
+ }
+ else if (strcmp (key, KEY_ACTIVATE_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_POWER_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_power_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOCK_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_lock_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_IDLE_ACTIVATION_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_idle_activation_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOCK_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_lock_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOCK_DISABLE) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_lock_disabled (prefs, disabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_USER_SWITCH_DISABLE) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_user_switch_disabled (prefs, disabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_CYCLE_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_cycle_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_KEYBOARD_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_keyboard_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_KEYBOARD_COMMAND) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_STRING)
+ {
+ const char *command;
+
+ command = mateconf_value_get_string (value);
+ _gs_prefs_set_keyboard_command (prefs, command);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_STATUS_MESSAGE_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_status_message_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOGOUT_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_logout_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOGOUT_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ _gs_prefs_set_logout_timeout (prefs, delay);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_LOGOUT_COMMAND) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_STRING)
+ {
+ const char *command;
+
+ command = mateconf_value_get_string (value);
+ _gs_prefs_set_logout_command (prefs, command);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else if (strcmp (key, KEY_USER_SWITCH_ENABLED) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+ _gs_prefs_set_user_switch_enabled (prefs, enabled);
+
+ changed = TRUE;
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else
+ {
+ g_warning ("Config key not handled: %s", key);
+ }
+
+ if (changed && prefs)
+ {
+ g_signal_emit (prefs, signals [CHANGED], 0);
+ }
+}
+
+static void
+gs_prefs_init (GSPrefs *prefs)
+{
+ prefs->priv = GS_PREFS_GET_PRIVATE (prefs);
+
+ prefs->priv->mateconf_client = mateconf_client_get_default ();
+
+ prefs->idle_activation_enabled = TRUE;
+ prefs->lock_enabled = TRUE;
+ prefs->lock_disabled = FALSE;
+ prefs->logout_enabled = FALSE;
+ prefs->user_switch_enabled = FALSE;
+
+ prefs->timeout = 600000;
+ prefs->power_timeout = 60000;
+ prefs->lock_timeout = 0;
+ prefs->logout_timeout = 14400000;
+ prefs->cycle = 600000;
+
+ prefs->mode = GS_MODE_SINGLE;
+
+ /* MateConf setup */
+ mateconf_client_add_dir (prefs->priv->mateconf_client,
+ KEY_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE, NULL);
+ mateconf_client_add_dir (prefs->priv->mateconf_client,
+ MATE_LOCKDOWN_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE, NULL);
+ mateconf_client_add_dir (prefs->priv->mateconf_client,
+ MATE_SESSION_DIR,
+ MATECONF_CLIENT_PRELOAD_NONE, NULL);
+
+
+ mateconf_client_notify_add (prefs->priv->mateconf_client,
+ KEY_DIR,
+ (MateConfClientNotifyFunc)key_changed_cb,
+ prefs,
+ NULL, NULL);
+ mateconf_client_notify_add (prefs->priv->mateconf_client,
+ MATE_LOCKDOWN_DIR,
+ (MateConfClientNotifyFunc)key_changed_cb,
+ prefs,
+ NULL, NULL);
+
+ gs_prefs_load_from_mateconf (prefs);
+}
+
+static void
+gs_prefs_finalize (GObject *object)
+{
+ GSPrefs *prefs;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_PREFS (object));
+
+ prefs = GS_PREFS (object);
+
+ g_return_if_fail (prefs->priv != NULL);
+
+ if (prefs->priv->mateconf_client)
+ {
+ g_object_unref (prefs->priv->mateconf_client);
+ prefs->priv->mateconf_client = NULL;
+ }
+
+ if (prefs->themes)
+ {
+ g_slist_foreach (prefs->themes, (GFunc)g_free, NULL);
+ g_slist_free (prefs->themes);
+ }
+
+ g_free (prefs->logout_command);
+ g_free (prefs->keyboard_command);
+
+ G_OBJECT_CLASS (gs_prefs_parent_class)->finalize (object);
+}
+
+GSPrefs *
+gs_prefs_new (void)
+{
+ GObject *prefs;
+
+ prefs = g_object_new (GS_TYPE_PREFS, NULL);
+
+ return GS_PREFS (prefs);
+}
diff --git a/src/gs-prefs.h b/src/gs-prefs.h
new file mode 100644
index 0000000..7a354dd
--- /dev/null
+++ b/src/gs-prefs.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_PREFS_H
+#define __GS_PREFS_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PREFS (gs_prefs_get_type ())
+#define GS_PREFS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_PREFS, GSPrefs))
+#define GS_PREFS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_PREFS, GSPrefsClass))
+#define GS_IS_PREFS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_PREFS))
+#define GS_IS_PREFS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_PREFS))
+#define GS_PREFS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_PREFS, GSPrefsClass))
+
+typedef enum
+{
+ GS_MODE_BLANK_ONLY,
+ GS_MODE_RANDOM,
+ GS_MODE_SINGLE
+} GSSaverMode;
+
+typedef struct GSPrefsPrivate GSPrefsPrivate;
+
+typedef struct
+{
+ GObject parent;
+
+ GSPrefsPrivate *priv;
+
+ guint idle_activation_enabled : 1; /* whether to activate when idle */
+ guint lock_enabled : 1; /* whether to lock when active */
+ guint logout_enabled : 1; /* Whether to offer the logout option */
+ guint lock_disabled : 1; /* Whether locking the system is disabled */
+ guint user_switch_disabled : 1; /* Whether user switching is disabled */
+ guint user_switch_enabled : 1; /* Whether to offer the user switch option */
+ guint keyboard_enabled : 1; /* Whether to try to embed a keyboard */
+ guint status_message_enabled : 1; /* show the status message in the lock */
+
+ guint power_timeout; /* how much idle time before power management */
+ guint timeout; /* how much idle time before activation */
+ guint lock_timeout; /* how long after activation locking starts */
+ guint logout_timeout; /* how long until the logout option appears */
+ guint cycle; /* how long each theme should run */
+
+ char *logout_command; /* command to use to logout */
+ char *keyboard_command; /* command to use to embed a keyboard */
+
+ GSList *themes; /* the screensaver themes to run */
+ GSSaverMode mode; /* theme selection mode */
+} GSPrefs;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* changed) (GSPrefs *prefs);
+} GSPrefsClass;
+
+GType gs_prefs_get_type (void);
+GSPrefs * gs_prefs_new (void);
+void gs_prefs_load (GSPrefs *prefs);
+
+G_END_DECLS
+
+#endif /* __GS_PREFS_H */
diff --git a/src/gs-prefs.h.orig b/src/gs-prefs.h.orig
new file mode 100644
index 0000000..3545471
--- /dev/null
+++ b/src/gs-prefs.h.orig
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_PREFS_H
+#define __GS_PREFS_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PREFS (gs_prefs_get_type ())
+#define GS_PREFS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_PREFS, GSPrefs))
+#define GS_PREFS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_PREFS, GSPrefsClass))
+#define GS_IS_PREFS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_PREFS))
+#define GS_IS_PREFS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_PREFS))
+#define GS_PREFS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_PREFS, GSPrefsClass))
+
+typedef enum
+{
+ GS_MODE_BLANK_ONLY,
+ GS_MODE_RANDOM,
+ GS_MODE_SINGLE
+} GSSaverMode;
+
+typedef struct GSPrefsPrivate GSPrefsPrivate;
+
+typedef struct
+{
+ GObject parent;
+
+ GSPrefsPrivate *priv;
+
+ guint idle_activation_enabled : 1; /* whether to activate when idle */
+ guint lock_enabled : 1; /* whether to lock when active */
+ guint logout_enabled : 1; /* Whether to offer the logout option */
+ guint lock_disabled : 1; /* Whether locking the system is disabled */
+ guint user_switch_disabled : 1; /* Whether user switching is disabled */
+ guint user_switch_enabled : 1; /* Whether to offer the user switch option */
+ guint keyboard_enabled : 1; /* Whether to try to embed a keyboard */
+ guint status_message_enabled : 1; /* show the status message in the lock */
+
+ guint power_timeout; /* how much idle time before power management */
+ guint timeout; /* how much idle time before activation */
+ guint lock_timeout; /* how long after activation locking starts */
+ guint logout_timeout; /* how long until the logout option appears */
+ guint cycle; /* how long each theme should run */
+
+ char *logout_command; /* command to use to logout */
+ char *keyboard_command; /* command to use to embed a keyboard */
+
+ GSList *themes; /* the screensaver themes to run */
+ GSSaverMode mode; /* theme selection mode */
+} GSPrefs;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* changed) (GSPrefs *prefs);
+} GSPrefsClass;
+
+GType gs_prefs_get_type (void);
+GSPrefs * gs_prefs_new (void);
+void gs_prefs_load (GSPrefs *prefs);
+
+G_END_DECLS
+
+#endif /* __GS_PREFS_H */
diff --git a/src/gs-theme-manager.c b/src/gs-theme-manager.c
new file mode 100644
index 0000000..32b086f
--- /dev/null
+++ b/src/gs-theme-manager.c
@@ -0,0 +1,453 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <glib-object.h>
+#include <matemenu-tree.h>
+
+#include "gs-theme-manager.h"
+#include "gs-debug.h"
+
+static void gs_theme_manager_class_init (GSThemeManagerClass *klass);
+static void gs_theme_manager_init (GSThemeManager *theme_manager);
+static void gs_theme_manager_finalize (GObject *object);
+
+#define GS_THEME_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_THEME_MANAGER, GSThemeManagerPrivate))
+
+struct _GSThemeInfo
+{
+ char *name;
+ char *exec;
+ char *file_id;
+ guint refcount;
+};
+
+struct GSThemeManagerPrivate
+{
+ MateMenuTree *menu_tree;
+};
+
+G_DEFINE_TYPE (GSThemeManager, gs_theme_manager, G_TYPE_OBJECT)
+
+static gpointer theme_manager_object = NULL;
+
+static const char *known_engine_locations [] =
+{
+ SAVERDIR,
+#ifdef XSCREENSAVER_HACK_DIR
+ XSCREENSAVER_HACK_DIR,
+#endif
+ LIBEXECDIR "/xscreensaver",
+ "/usr/libexec/xscreensaver",
+ "/usr/lib/xscreensaver",
+ NULL
+};
+
+/* Returns the full path to the queried command */
+static char *
+find_command (const char *command)
+{
+ int i;
+
+ if (g_path_is_absolute (command))
+ {
+ char *dirname;
+
+ dirname = g_path_get_dirname (command);
+ for (i = 0; known_engine_locations [i]; i++)
+ {
+ if (strcmp (dirname, known_engine_locations [i]) == 0)
+ {
+ if (g_file_test (command, G_FILE_TEST_IS_EXECUTABLE)
+ && ! g_file_test (command, G_FILE_TEST_IS_DIR))
+ {
+ g_free (dirname);
+ return g_strdup (command);
+ }
+ }
+ }
+ g_free (dirname);
+ }
+ else
+ {
+ for (i = 0; known_engine_locations [i]; i++)
+ {
+ char *path;
+
+ path = g_build_filename (known_engine_locations [i], command, NULL);
+
+ if (g_file_test (path, G_FILE_TEST_IS_EXECUTABLE)
+ && ! g_file_test (path, G_FILE_TEST_IS_DIR))
+ {
+ return path;
+ }
+
+ g_free (path);
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+check_command (const char *command)
+{
+ char *path;
+ char **argv;
+
+ g_return_val_if_fail (command != NULL, FALSE);
+
+ g_shell_parse_argv (command, NULL, &argv, NULL);
+ path = find_command (argv [0]);
+ g_strfreev (argv);
+
+ if (path != NULL)
+ {
+ g_free (path);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+add_known_engine_locations_to_path (void)
+{
+ static gboolean already_added;
+ int i;
+ GString *str;
+
+ /* We only want to add the items to the path once */
+ if (already_added)
+ {
+ return;
+ }
+
+ already_added = TRUE;
+
+ /* TODO: set a default PATH ? */
+
+ str = g_string_new (g_getenv ("PATH"));
+ for (i = 0; known_engine_locations [i]; i++)
+ {
+ /* TODO: check that permissions are safe */
+ if (g_file_test (known_engine_locations [i], G_FILE_TEST_IS_DIR))
+ {
+ g_string_append_printf (str, ":%s", known_engine_locations [i]);
+ }
+ }
+
+ g_setenv ("PATH", str->str, TRUE);
+ g_string_free (str, TRUE);
+}
+
+GSThemeInfo *
+gs_theme_info_ref (GSThemeInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (info->refcount > 0, NULL);
+
+ info->refcount++;
+
+ return info;
+}
+
+void
+gs_theme_info_unref (GSThemeInfo *info)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->refcount > 0);
+
+ if (--info->refcount == 0)
+ {
+ g_free (info->name);
+ g_free (info->exec);
+ g_free (info->file_id);
+
+ g_free (info);
+ }
+}
+
+const char *
+gs_theme_info_get_id (GSThemeInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->file_id;
+}
+
+const char *
+gs_theme_info_get_name (GSThemeInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->name;
+}
+
+const char *
+gs_theme_info_get_exec (GSThemeInfo *info)
+{
+ const char *exec;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (check_command (info->exec))
+ {
+ exec = info->exec;
+ }
+ else
+ {
+ exec = NULL;
+ }
+
+ return exec;
+}
+
+static GSThemeInfo *
+gs_theme_info_new_from_matemenu_tree_entry (MateMenuTreeEntry *entry)
+{
+ GSThemeInfo *info;
+ const char *str;
+ char *pos;
+
+ info = g_new0 (GSThemeInfo, 1);
+
+ info->refcount = 1;
+ info->name = g_strdup (matemenu_tree_entry_get_name (entry));
+ info->exec = g_strdup (matemenu_tree_entry_get_exec (entry));
+
+ /* remove the .desktop suffix */
+ str = matemenu_tree_entry_get_desktop_file_id (entry);
+ pos = g_strrstr (str, ".desktop");
+ if (pos)
+ {
+ info->file_id = g_strndup (str, pos - str);
+ }
+ else
+ {
+ info->file_id = g_strdup (str);
+ }
+
+ return info;
+}
+
+static GSThemeInfo *
+find_info_for_id (MateMenuTree *tree,
+ const char *id)
+{
+ GSThemeInfo *info;
+ MateMenuTreeDirectory *root;
+ GSList *items;
+ GSList *l;
+
+ root = matemenu_tree_get_root_directory (tree);
+ if (root == NULL)
+ {
+ return NULL;
+ }
+
+ items = matemenu_tree_directory_get_contents (root);
+
+ info = NULL;
+
+ for (l = items; l; l = l->next)
+ {
+ if (info == NULL
+ && matemenu_tree_item_get_type (l->data) == MATEMENU_TREE_ITEM_ENTRY)
+ {
+ MateMenuTreeEntry *entry = l->data;
+ const char *file_id;
+
+ file_id = matemenu_tree_entry_get_desktop_file_id (entry);
+ if (file_id && id && strcmp (file_id, id) == 0)
+ {
+ info = gs_theme_info_new_from_matemenu_tree_entry (entry);
+ }
+ }
+
+ matemenu_tree_item_unref (l->data);
+ }
+
+ g_slist_free (items);
+ matemenu_tree_item_unref (root);
+
+ return info;
+}
+
+GSThemeInfo *
+gs_theme_manager_lookup_theme_info (GSThemeManager *theme_manager,
+ const char *name)
+{
+ GSThemeInfo *info;
+ char *id;
+
+ g_return_val_if_fail (GS_IS_THEME_MANAGER (theme_manager), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ id = g_strdup_printf ("%s.desktop", name);
+ info = find_info_for_id (theme_manager->priv->menu_tree, id);
+ g_free (id);
+
+ return info;
+}
+
+static void
+theme_prepend_entry (GSList **parent_list,
+ MateMenuTreeEntry *entry,
+ const char *filename)
+{
+ GSThemeInfo *info;
+
+ info = gs_theme_info_new_from_matemenu_tree_entry (entry);
+
+ *parent_list = g_slist_prepend (*parent_list, info);
+}
+
+static void
+make_theme_list (GSList **parent_list,
+ MateMenuTreeDirectory *directory,
+ const char *filename)
+{
+ GSList *items;
+ GSList *l;
+
+ items = matemenu_tree_directory_get_contents (directory);
+
+ for (l = items; l; l = l->next)
+ {
+ switch (matemenu_tree_item_get_type (l->data))
+ {
+
+ case MATEMENU_TREE_ITEM_ENTRY:
+ theme_prepend_entry (parent_list, l->data, filename);
+ break;
+
+ case MATEMENU_TREE_ITEM_ALIAS:
+ case MATEMENU_TREE_ITEM_DIRECTORY:
+ default:
+ break;
+ }
+
+ matemenu_tree_item_unref (l->data);
+ }
+
+ g_slist_free (items);
+
+ *parent_list = g_slist_reverse (*parent_list);
+}
+
+GSList *
+gs_theme_manager_get_info_list (GSThemeManager *theme_manager)
+{
+ GSList *l = NULL;
+ MateMenuTreeDirectory *root;
+
+ g_return_val_if_fail (GS_IS_THEME_MANAGER (theme_manager), NULL);
+
+ root = matemenu_tree_get_root_directory (theme_manager->priv->menu_tree);
+
+ if (root != NULL)
+ {
+ make_theme_list (&l, root, "mate-screensavers.menu");
+ matemenu_tree_item_unref (root);
+ }
+
+ return l;
+}
+
+static void
+gs_theme_manager_class_init (GSThemeManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_theme_manager_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSThemeManagerPrivate));
+}
+
+static MateMenuTree *
+get_themes_tree (void)
+{
+ MateMenuTree *themes_tree = NULL;
+
+ /* we only need to add the locations to the path once
+ and since this is only run once we'll do it here */
+ add_known_engine_locations_to_path ();
+
+ themes_tree = matemenu_tree_lookup ("mate-screensavers.menu", MATEMENU_TREE_FLAGS_NONE);
+
+ return themes_tree;
+}
+
+static void
+gs_theme_manager_init (GSThemeManager *theme_manager)
+{
+ theme_manager->priv = GS_THEME_MANAGER_GET_PRIVATE (theme_manager);
+
+ theme_manager->priv->menu_tree = get_themes_tree ();
+}
+
+static void
+gs_theme_manager_finalize (GObject *object)
+{
+ GSThemeManager *theme_manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_THEME_MANAGER (object));
+
+ theme_manager = GS_THEME_MANAGER (object);
+
+ g_return_if_fail (theme_manager->priv != NULL);
+
+ if (theme_manager->priv->menu_tree != NULL)
+ {
+ matemenu_tree_unref (theme_manager->priv->menu_tree);
+ }
+
+ G_OBJECT_CLASS (gs_theme_manager_parent_class)->finalize (object);
+}
+
+GSThemeManager *
+gs_theme_manager_new (void)
+{
+ if (theme_manager_object)
+ {
+ g_object_ref (theme_manager_object);
+ }
+ else
+ {
+ theme_manager_object = g_object_new (GS_TYPE_THEME_MANAGER, NULL);
+ g_object_add_weak_pointer (theme_manager_object,
+ (gpointer *) &theme_manager_object);
+ }
+
+ return GS_THEME_MANAGER (theme_manager_object);
+}
diff --git a/src/gs-theme-manager.c.orig b/src/gs-theme-manager.c.orig
new file mode 100644
index 0000000..48b3c41
--- /dev/null
+++ b/src/gs-theme-manager.c.orig
@@ -0,0 +1,453 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include <glib-object.h>
+#include <matemenu-tree.h>
+
+#include "gs-theme-manager.h"
+#include "gs-debug.h"
+
+static void gs_theme_manager_class_init (GSThemeManagerClass *klass);
+static void gs_theme_manager_init (GSThemeManager *theme_manager);
+static void gs_theme_manager_finalize (GObject *object);
+
+#define GS_THEME_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_THEME_MANAGER, GSThemeManagerPrivate))
+
+struct _GSThemeInfo
+{
+ char *name;
+ char *exec;
+ char *file_id;
+ guint refcount;
+};
+
+struct GSThemeManagerPrivate
+{
+ MateMenuTree *menu_tree;
+};
+
+G_DEFINE_TYPE (GSThemeManager, gs_theme_manager, G_TYPE_OBJECT)
+
+static gpointer theme_manager_object = NULL;
+
+static const char *known_engine_locations [] =
+{
+ SAVERDIR,
+#ifdef XSCREENSAVER_HACK_DIR
+ XSCREENSAVER_HACK_DIR,
+#endif
+ LIBEXECDIR "/xscreensaver",
+ "/usr/libexec/xscreensaver",
+ "/usr/lib/xscreensaver",
+ NULL
+};
+
+/* Returns the full path to the queried command */
+static char *
+find_command (const char *command)
+{
+ int i;
+
+ if (g_path_is_absolute (command))
+ {
+ char *dirname;
+
+ dirname = g_path_get_dirname (command);
+ for (i = 0; known_engine_locations [i]; i++)
+ {
+ if (strcmp (dirname, known_engine_locations [i]) == 0)
+ {
+ if (g_file_test (command, G_FILE_TEST_IS_EXECUTABLE)
+ && ! g_file_test (command, G_FILE_TEST_IS_DIR))
+ {
+ g_free (dirname);
+ return g_strdup (command);
+ }
+ }
+ }
+ g_free (dirname);
+ }
+ else
+ {
+ for (i = 0; known_engine_locations [i]; i++)
+ {
+ char *path;
+
+ path = g_build_filename (known_engine_locations [i], command, NULL);
+
+ if (g_file_test (path, G_FILE_TEST_IS_EXECUTABLE)
+ && ! g_file_test (path, G_FILE_TEST_IS_DIR))
+ {
+ return path;
+ }
+
+ g_free (path);
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+check_command (const char *command)
+{
+ char *path;
+ char **argv;
+
+ g_return_val_if_fail (command != NULL, FALSE);
+
+ g_shell_parse_argv (command, NULL, &argv, NULL);
+ path = find_command (argv [0]);
+ g_strfreev (argv);
+
+ if (path != NULL)
+ {
+ g_free (path);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+add_known_engine_locations_to_path (void)
+{
+ static gboolean already_added;
+ int i;
+ GString *str;
+
+ /* We only want to add the items to the path once */
+ if (already_added)
+ {
+ return;
+ }
+
+ already_added = TRUE;
+
+ /* TODO: set a default PATH ? */
+
+ str = g_string_new (g_getenv ("PATH"));
+ for (i = 0; known_engine_locations [i]; i++)
+ {
+ /* TODO: check that permissions are safe */
+ if (g_file_test (known_engine_locations [i], G_FILE_TEST_IS_DIR))
+ {
+ g_string_append_printf (str, ":%s", known_engine_locations [i]);
+ }
+ }
+
+ g_setenv ("PATH", str->str, TRUE);
+ g_string_free (str, TRUE);
+}
+
+GSThemeInfo *
+gs_theme_info_ref (GSThemeInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+ g_return_val_if_fail (info->refcount > 0, NULL);
+
+ info->refcount++;
+
+ return info;
+}
+
+void
+gs_theme_info_unref (GSThemeInfo *info)
+{
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (info->refcount > 0);
+
+ if (--info->refcount == 0)
+ {
+ g_free (info->name);
+ g_free (info->exec);
+ g_free (info->file_id);
+
+ g_free (info);
+ }
+}
+
+const char *
+gs_theme_info_get_id (GSThemeInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->file_id;
+}
+
+const char *
+gs_theme_info_get_name (GSThemeInfo *info)
+{
+ g_return_val_if_fail (info != NULL, NULL);
+
+ return info->name;
+}
+
+const char *
+gs_theme_info_get_exec (GSThemeInfo *info)
+{
+ const char *exec;
+
+ g_return_val_if_fail (info != NULL, NULL);
+
+ if (check_command (info->exec))
+ {
+ exec = info->exec;
+ }
+ else
+ {
+ exec = NULL;
+ }
+
+ return exec;
+}
+
+static GSThemeInfo *
+gs_theme_info_new_from_matemenu_tree_entry (MateMenuTreeEntry *entry)
+{
+ GSThemeInfo *info;
+ const char *str;
+ char *pos;
+
+ info = g_new0 (GSThemeInfo, 1);
+
+ info->refcount = 1;
+ info->name = g_strdup (matemenu_tree_entry_get_name (entry));
+ info->exec = g_strdup (matemenu_tree_entry_get_exec (entry));
+
+ /* remove the .desktop suffix */
+ str = matemenu_tree_entry_get_desktop_file_id (entry);
+ pos = g_strrstr (str, ".desktop");
+ if (pos)
+ {
+ info->file_id = g_strndup (str, pos - str);
+ }
+ else
+ {
+ info->file_id = g_strdup (str);
+ }
+
+ return info;
+}
+
+static GSThemeInfo *
+find_info_for_id (MateMenuTree *tree,
+ const char *id)
+{
+ GSThemeInfo *info;
+ MateMenuTreeDirectory *root;
+ GSList *items;
+ GSList *l;
+
+ root = matemenu_tree_get_root_directory (tree);
+ if (root == NULL)
+ {
+ return NULL;
+ }
+
+ items = matemenu_tree_directory_get_contents (root);
+
+ info = NULL;
+
+ for (l = items; l; l = l->next)
+ {
+ if (info == NULL
+ && matemenu_tree_item_get_type (l->data) == MATEMENU_TREE_ITEM_ENTRY)
+ {
+ MateMenuTreeEntry *entry = l->data;
+ const char *file_id;
+
+ file_id = matemenu_tree_entry_get_desktop_file_id (entry);
+ if (file_id && id && strcmp (file_id, id) == 0)
+ {
+ info = gs_theme_info_new_from_matemenu_tree_entry (entry);
+ }
+ }
+
+ matemenu_tree_item_unref (l->data);
+ }
+
+ g_slist_free (items);
+ matemenu_tree_item_unref (root);
+
+ return info;
+}
+
+GSThemeInfo *
+gs_theme_manager_lookup_theme_info (GSThemeManager *theme_manager,
+ const char *name)
+{
+ GSThemeInfo *info;
+ char *id;
+
+ g_return_val_if_fail (GS_IS_THEME_MANAGER (theme_manager), NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ id = g_strdup_printf ("%s.desktop", name);
+ info = find_info_for_id (theme_manager->priv->menu_tree, id);
+ g_free (id);
+
+ return info;
+}
+
+static void
+theme_prepend_entry (GSList **parent_list,
+ MateMenuTreeEntry *entry,
+ const char *filename)
+{
+ GSThemeInfo *info;
+
+ info = gs_theme_info_new_from_matemenu_tree_entry (entry);
+
+ *parent_list = g_slist_prepend (*parent_list, info);
+}
+
+static void
+make_theme_list (GSList **parent_list,
+ MateMenuTreeDirectory *directory,
+ const char *filename)
+{
+ GSList *items;
+ GSList *l;
+
+ items = matemenu_tree_directory_get_contents (directory);
+
+ for (l = items; l; l = l->next)
+ {
+ switch (matemenu_tree_item_get_type (l->data))
+ {
+
+ case MATEMENU_TREE_ITEM_ENTRY:
+ theme_prepend_entry (parent_list, l->data, filename);
+ break;
+
+ case MATEMENU_TREE_ITEM_ALIAS:
+ case MATEMENU_TREE_ITEM_DIRECTORY:
+ default:
+ break;
+ }
+
+ matemenu_tree_item_unref (l->data);
+ }
+
+ g_slist_free (items);
+
+ *parent_list = g_slist_reverse (*parent_list);
+}
+
+GSList *
+gs_theme_manager_get_info_list (GSThemeManager *theme_manager)
+{
+ GSList *l = NULL;
+ MateMenuTreeDirectory *root;
+
+ g_return_val_if_fail (GS_IS_THEME_MANAGER (theme_manager), NULL);
+
+ root = matemenu_tree_get_root_directory (theme_manager->priv->menu_tree);
+
+ if (root != NULL)
+ {
+ make_theme_list (&l, root, "mate-screensavers.menu");
+ matemenu_tree_item_unref (root);
+ }
+
+ return l;
+}
+
+static void
+gs_theme_manager_class_init (GSThemeManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_theme_manager_finalize;
+
+ g_type_class_add_private (klass, sizeof (GSThemeManagerPrivate));
+}
+
+static MateMenuTree *
+get_themes_tree (void)
+{
+ MateMenuTree *themes_tree = NULL;
+
+ /* we only need to add the locations to the path once
+ and since this is only run once we'll do it here */
+ add_known_engine_locations_to_path ();
+
+ themes_tree = matemenu_tree_lookup ("mate-screensavers.menu", MATEMENU_TREE_FLAGS_NONE);
+
+ return themes_tree;
+}
+
+static void
+gs_theme_manager_init (GSThemeManager *theme_manager)
+{
+ theme_manager->priv = GS_THEME_MANAGER_GET_PRIVATE (theme_manager);
+
+ theme_manager->priv->menu_tree = get_themes_tree ();
+}
+
+static void
+gs_theme_manager_finalize (GObject *object)
+{
+ GSThemeManager *theme_manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_THEME_MANAGER (object));
+
+ theme_manager = GS_THEME_MANAGER (object);
+
+ g_return_if_fail (theme_manager->priv != NULL);
+
+ if (theme_manager->priv->menu_tree != NULL)
+ {
+ matemenu_tree_unref (theme_manager->priv->menu_tree);
+ }
+
+ G_OBJECT_CLASS (gs_theme_manager_parent_class)->finalize (object);
+}
+
+GSThemeManager *
+gs_theme_manager_new (void)
+{
+ if (theme_manager_object)
+ {
+ g_object_ref (theme_manager_object);
+ }
+ else
+ {
+ theme_manager_object = g_object_new (GS_TYPE_THEME_MANAGER, NULL);
+ g_object_add_weak_pointer (theme_manager_object,
+ (gpointer *) &theme_manager_object);
+ }
+
+ return GS_THEME_MANAGER (theme_manager_object);
+}
diff --git a/src/gs-theme-manager.h b/src/gs-theme-manager.h
new file mode 100644
index 0000000..5b79309
--- /dev/null
+++ b/src/gs-theme-manager.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_THEME_MANAGER_H
+#define __GS_THEME_MANAGER_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_THEME_MANAGER (gs_theme_manager_get_type ())
+#define GS_THEME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_THEME_MANAGER, GSThemeManager))
+#define GS_THEME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_THEME_MANAGER, GSThemeManagerClass))
+#define GS_IS_THEME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_THEME_MANAGER))
+#define GS_IS_THEME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_THEME_MANAGER))
+#define GS_THEME_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_THEME_MANAGER, GSThemeManagerClass))
+
+typedef struct GSThemeManagerPrivate GSThemeManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSThemeManagerPrivate *priv;
+} GSThemeManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GSThemeManagerClass;
+
+typedef struct _GSThemeInfo GSThemeInfo;
+
+GType gs_theme_manager_get_type (void);
+
+GSThemeManager *gs_theme_manager_new (void);
+
+GSList *gs_theme_manager_get_info_list (GSThemeManager *manager);
+GSThemeInfo *gs_theme_manager_lookup_theme_info (GSThemeManager *manager,
+ const char *theme);
+GSThemeInfo *gs_theme_info_ref (GSThemeInfo *info);
+void gs_theme_info_unref (GSThemeInfo *info);
+const char *gs_theme_info_get_id (GSThemeInfo *info);
+const char *gs_theme_info_get_name (GSThemeInfo *info);
+const char *gs_theme_info_get_exec (GSThemeInfo *info);
+
+G_END_DECLS
+
+#endif /* __GS_THEME_MANAGER_H */
diff --git a/src/gs-theme-manager.h.orig b/src/gs-theme-manager.h.orig
new file mode 100644
index 0000000..8472961
--- /dev/null
+++ b/src/gs-theme-manager.h.orig
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_THEME_MANAGER_H
+#define __GS_THEME_MANAGER_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_THEME_MANAGER (gs_theme_manager_get_type ())
+#define GS_THEME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_THEME_MANAGER, GSThemeManager))
+#define GS_THEME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_THEME_MANAGER, GSThemeManagerClass))
+#define GS_IS_THEME_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_THEME_MANAGER))
+#define GS_IS_THEME_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_THEME_MANAGER))
+#define GS_THEME_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_THEME_MANAGER, GSThemeManagerClass))
+
+typedef struct GSThemeManagerPrivate GSThemeManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSThemeManagerPrivate *priv;
+} GSThemeManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+} GSThemeManagerClass;
+
+typedef struct _GSThemeInfo GSThemeInfo;
+
+GType gs_theme_manager_get_type (void);
+
+GSThemeManager *gs_theme_manager_new (void);
+
+GSList *gs_theme_manager_get_info_list (GSThemeManager *manager);
+GSThemeInfo *gs_theme_manager_lookup_theme_info (GSThemeManager *manager,
+ const char *theme);
+GSThemeInfo *gs_theme_info_ref (GSThemeInfo *info);
+void gs_theme_info_unref (GSThemeInfo *info);
+const char *gs_theme_info_get_id (GSThemeInfo *info);
+const char *gs_theme_info_get_name (GSThemeInfo *info);
+const char *gs_theme_info_get_exec (GSThemeInfo *info);
+
+G_END_DECLS
+
+#endif /* __GS_THEME_MANAGER_H */
diff --git a/src/gs-visual-gl.c b/src/gs-visual-gl.c
new file mode 100644
index 0000000..7a71c79
--- /dev/null
+++ b/src/gs-visual-gl.c
@@ -0,0 +1,116 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ * Copyright (C) 1999, 2000, 2003 Jamie Zawinski <[email protected]>
+ *
+ * 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 <unistd.h>
+#include <string.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_LIBGL
+#include <GL/gl.h>
+#include <GL/glx.h>
+#endif /* HAVE_GL */
+
+#include "gs-visual-gl.h"
+#include "gs-debug.h"
+
+GdkVisual *
+gs_visual_gl_get_best_for_screen (GdkScreen *screen)
+{
+ GdkVisual *visual;
+#ifdef HAVE_LIBGL
+ GdkDisplay *display;
+ int screen_num;
+ int i;
+
+# define R GLX_RED_SIZE
+# define G GLX_GREEN_SIZE
+# define B GLX_BLUE_SIZE
+# define D GLX_DEPTH_SIZE
+# define I GLX_BUFFER_SIZE
+# define DB GLX_DOUBLEBUFFER
+# define ST GLX_STENCIL_SIZE
+
+ static int attrs [][20] =
+ {
+ { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, DB, ST,1, 0 }, /* rgb double, stencil */
+ { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, DB, ST,1, 0 },
+ { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, DB, ST,1, 0 },
+ { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, DB, 0 }, /* rgb double */
+ { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, DB, 0 },
+ { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, DB, 0 },
+ { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, 0 }, /* rgb single */
+ { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, 0 },
+ { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, 0 },
+ { I, 8, D, 8, DB, 0 }, /* cmap double */
+ { I, 4, D, 4, DB, 0 },
+ { I, 8, D, 8, 0 }, /* cmap single */
+ { I, 4, D, 4, 0 },
+ { GLX_RGBA, R, 1, G, 1, B, 1, D, 1, 0 } /* monochrome */
+ };
+
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ display = gdk_screen_get_display (screen);
+ screen_num = gdk_screen_get_number (screen);
+
+ gdk_error_trap_push ();
+
+ visual = NULL;
+ for (i = 0; i < G_N_ELEMENTS (attrs); i++)
+ {
+ XVisualInfo *vi;
+
+ vi = glXChooseVisual (GDK_DISPLAY_XDISPLAY (display), screen_num, attrs [i]);
+
+ if (vi != NULL)
+ {
+ VisualID vid;
+
+ vid = XVisualIDFromVisual (vi->visual);
+
+ visual = gdkx_visual_get (vid);
+
+ XFree (vi);
+
+ if (visual != NULL)
+ {
+ break;
+ }
+ }
+ }
+
+ gdk_display_sync (display);
+ gdk_error_trap_pop ();
+
+#else
+ visual = NULL;
+
+#endif /* HAVE_LIBGL */
+
+ return visual;
+}
diff --git a/src/gs-visual-gl.c.orig b/src/gs-visual-gl.c.orig
new file mode 100644
index 0000000..e253b28
--- /dev/null
+++ b/src/gs-visual-gl.c.orig
@@ -0,0 +1,116 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ * Copyright (C) 1999, 2000, 2003 Jamie Zawinski <[email protected]>
+ *
+ * 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 <unistd.h>
+#include <string.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#ifdef HAVE_LIBGL
+#include <GL/gl.h>
+#include <GL/glx.h>
+#endif /* HAVE_GL */
+
+#include "gs-visual-gl.h"
+#include "gs-debug.h"
+
+GdkVisual *
+gs_visual_gl_get_best_for_screen (GdkScreen *screen)
+{
+ GdkVisual *visual;
+#ifdef HAVE_LIBGL
+ GdkDisplay *display;
+ int screen_num;
+ int i;
+
+# define R GLX_RED_SIZE
+# define G GLX_GREEN_SIZE
+# define B GLX_BLUE_SIZE
+# define D GLX_DEPTH_SIZE
+# define I GLX_BUFFER_SIZE
+# define DB GLX_DOUBLEBUFFER
+# define ST GLX_STENCIL_SIZE
+
+ static int attrs [][20] =
+ {
+ { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, DB, ST,1, 0 }, /* rgb double, stencil */
+ { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, DB, ST,1, 0 },
+ { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, DB, ST,1, 0 },
+ { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, DB, 0 }, /* rgb double */
+ { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, DB, 0 },
+ { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, DB, 0 },
+ { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, 0 }, /* rgb single */
+ { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, 0 },
+ { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, 0 },
+ { I, 8, D, 8, DB, 0 }, /* cmap double */
+ { I, 4, D, 4, DB, 0 },
+ { I, 8, D, 8, 0 }, /* cmap single */
+ { I, 4, D, 4, 0 },
+ { GLX_RGBA, R, 1, G, 1, B, 1, D, 1, 0 } /* monochrome */
+ };
+
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ display = gdk_screen_get_display (screen);
+ screen_num = gdk_screen_get_number (screen);
+
+ gdk_error_trap_push ();
+
+ visual = NULL;
+ for (i = 0; i < G_N_ELEMENTS (attrs); i++)
+ {
+ XVisualInfo *vi;
+
+ vi = glXChooseVisual (GDK_DISPLAY_XDISPLAY (display), screen_num, attrs [i]);
+
+ if (vi != NULL)
+ {
+ VisualID vid;
+
+ vid = XVisualIDFromVisual (vi->visual);
+
+ visual = gdkx_visual_get (vid);
+
+ XFree (vi);
+
+ if (visual != NULL)
+ {
+ break;
+ }
+ }
+ }
+
+ gdk_display_sync (display);
+ gdk_error_trap_pop ();
+
+#else
+ visual = NULL;
+
+#endif /* HAVE_LIBGL */
+
+ return visual;
+}
diff --git a/src/gs-visual-gl.h b/src/gs-visual-gl.h
new file mode 100644
index 0000000..fca138c
--- /dev/null
+++ b/src/gs-visual-gl.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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 __GS_VISUAL_GL_H
+#define __GS_VISUAL_GL_H
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+GdkVisual *gs_visual_gl_get_best_for_screen (GdkScreen *screen);
+
+G_END_DECLS
+
+#endif /* __GS_VISUAL_GL_H */
diff --git a/src/gs-watcher-x11.c b/src/gs-watcher-x11.c
new file mode 100644
index 0000000..1e3114e
--- /dev/null
+++ b/src/gs-watcher-x11.c
@@ -0,0 +1,680 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <gdk/gdkx.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+
+#include "gs-watcher.h"
+#include "gs-marshal.h"
+#include "gs-debug.h"
+
+static void gs_watcher_class_init (GSWatcherClass *klass);
+static void gs_watcher_init (GSWatcher *watcher);
+static void gs_watcher_finalize (GObject *object);
+
+static gboolean watchdog_timer (GSWatcher *watcher);
+
+#define GS_WATCHER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_WATCHER, GSWatcherPrivate))
+
+struct GSWatcherPrivate
+{
+ /* settings */
+ guint enabled : 1;
+ guint delta_notice_timeout;
+
+ /* state */
+ guint active : 1;
+ guint idle : 1;
+ guint idle_notice : 1;
+
+ guint idle_id;
+ char *status_message;
+
+ DBusGProxy *presence_proxy;
+ guint watchdog_timer_id;
+};
+
+enum
+{
+ PROP_0,
+ PROP_STATUS_MESSAGE
+};
+
+enum
+{
+ IDLE_CHANGED,
+ IDLE_NOTICE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSWatcher, gs_watcher, G_TYPE_OBJECT)
+
+static void
+remove_watchdog_timer (GSWatcher *watcher)
+{
+ if (watcher->priv->watchdog_timer_id != 0)
+ {
+ g_source_remove (watcher->priv->watchdog_timer_id);
+ watcher->priv->watchdog_timer_id = 0;
+ }
+}
+
+static void
+add_watchdog_timer (GSWatcher *watcher,
+ glong timeout)
+{
+ watcher->priv->watchdog_timer_id = g_timeout_add (timeout,
+ (GSourceFunc)watchdog_timer,
+ watcher);
+}
+
+static void
+gs_watcher_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSWatcher *self;
+
+ self = GS_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_status_text (GSWatcher *watcher,
+ const char *text)
+{
+ g_free (watcher->priv->status_message);
+
+ watcher->priv->status_message = g_strdup (text);
+ g_object_notify (G_OBJECT (watcher), "status-message");
+}
+
+static void
+gs_watcher_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSWatcher *self;
+
+ self = GS_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_STATUS_MESSAGE:
+ set_status_text (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_watcher_class_init (GSWatcherClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_watcher_finalize;
+ object_class->get_property = gs_watcher_get_property;
+ object_class->set_property = gs_watcher_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_STATUS_MESSAGE,
+ g_param_spec_string ("status-message",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ signals [IDLE_CHANGED] =
+ g_signal_new ("idle-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWatcherClass, idle_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1, G_TYPE_BOOLEAN);
+ signals [IDLE_NOTICE_CHANGED] =
+ g_signal_new ("idle-notice-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWatcherClass, idle_notice_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1, G_TYPE_BOOLEAN);
+
+ g_type_class_add_private (klass, sizeof (GSWatcherPrivate));
+}
+
+static gboolean
+_gs_watcher_set_session_idle_notice (GSWatcher *watcher,
+ gboolean in_effect)
+{
+ gboolean res;
+
+ res = FALSE;
+
+ if (in_effect != watcher->priv->idle_notice)
+ {
+
+ g_signal_emit (watcher, signals [IDLE_NOTICE_CHANGED], 0, in_effect, &res);
+ if (res)
+ {
+ gs_debug ("Changing idle notice state: %d", in_effect);
+
+ watcher->priv->idle_notice = in_effect;
+ }
+ else
+ {
+ gs_debug ("Idle notice signal not handled: %d", in_effect);
+ }
+ }
+
+ return res;
+}
+
+static gboolean
+_gs_watcher_set_session_idle (GSWatcher *watcher,
+ gboolean is_idle)
+{
+ gboolean res;
+
+ res = FALSE;
+
+ if (is_idle != watcher->priv->idle)
+ {
+
+ g_signal_emit (watcher, signals [IDLE_CHANGED], 0, is_idle, &res);
+ if (res)
+ {
+ gs_debug ("Changing idle state: %d", is_idle);
+
+ watcher->priv->idle = is_idle;
+ }
+ else
+ {
+ gs_debug ("Idle changed signal not handled: %d", is_idle);
+ }
+ }
+
+ return res;
+}
+
+gboolean
+gs_watcher_get_active (GSWatcher *watcher)
+{
+ gboolean active;
+
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ active = watcher->priv->active;
+
+ return active;
+}
+
+static void
+_gs_watcher_reset_state (GSWatcher *watcher)
+{
+ watcher->priv->idle = FALSE;
+ watcher->priv->idle_notice = FALSE;
+}
+
+static gboolean
+_gs_watcher_set_active_internal (GSWatcher *watcher,
+ gboolean active)
+{
+ if (active != watcher->priv->active)
+ {
+ /* reset state */
+ _gs_watcher_reset_state (watcher);
+
+ watcher->priv->active = active;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_watcher_set_active (GSWatcher *watcher,
+ gboolean active)
+{
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ gs_debug ("turning watcher: %s", active ? "ON" : "OFF");
+
+ if (watcher->priv->active == active)
+ {
+ gs_debug ("Idle detection is already %s",
+ active ? "active" : "inactive");
+ return FALSE;
+ }
+
+ if (! watcher->priv->enabled)
+ {
+ gs_debug ("Idle detection is disabled, cannot activate");
+ return FALSE;
+ }
+
+ return _gs_watcher_set_active_internal (watcher, active);
+}
+
+gboolean
+gs_watcher_set_enabled (GSWatcher *watcher,
+ gboolean enabled)
+{
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ if (watcher->priv->enabled != enabled)
+ {
+ gboolean is_active = gs_watcher_get_active (watcher);
+
+ watcher->priv->enabled = enabled;
+
+ /* if we are disabling the watcher and we are
+ active shut it down */
+ if (! enabled && is_active)
+ {
+ _gs_watcher_set_active_internal (watcher, FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_watcher_get_enabled (GSWatcher *watcher)
+{
+ gboolean enabled;
+
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ enabled = watcher->priv->enabled;
+
+ return enabled;
+}
+
+static gboolean
+on_idle_timeout (GSWatcher *watcher)
+{
+ gboolean res;
+
+ res = _gs_watcher_set_session_idle (watcher, TRUE);
+
+ _gs_watcher_set_session_idle_notice (watcher, FALSE);
+
+ /* try again if we failed i guess */
+ return !res;
+}
+
+static void
+set_status (GSWatcher *watcher,
+ guint status)
+{
+ gboolean res;
+ gboolean is_idle;
+
+ if (! watcher->priv->active)
+ {
+ gs_debug ("GSWatcher: not active, ignoring status changes");
+ return;
+ }
+
+ is_idle = (status == 3);
+
+ if (!is_idle && !watcher->priv->idle_notice)
+ {
+ /* no change in idleness */
+ return;
+ }
+
+ if (is_idle)
+ {
+ res = _gs_watcher_set_session_idle_notice (watcher, is_idle);
+ /* queue an activation */
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+ watcher->priv->idle_id = g_timeout_add (watcher->priv->delta_notice_timeout,
+ (GSourceFunc)on_idle_timeout,
+ watcher);
+ }
+ else
+ {
+ /* cancel notice too */
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+ res = _gs_watcher_set_session_idle (watcher, FALSE);
+ res = _gs_watcher_set_session_idle_notice (watcher, FALSE);
+ }
+}
+
+static void
+on_presence_status_changed (DBusGProxy *presence_proxy,
+ guint status,
+ GSWatcher *watcher)
+{
+ set_status (watcher, status);
+}
+
+static void
+on_presence_status_text_changed (DBusGProxy *presence_proxy,
+ const char *status_text,
+ GSWatcher *watcher)
+{
+ set_status_text (watcher, status_text);
+}
+
+static gboolean
+connect_presence_watcher (GSWatcher *watcher)
+{
+ DBusGConnection *bus;
+ GError *error;
+ gboolean ret;
+
+ ret = FALSE;
+
+ error = NULL;
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ if (bus == NULL)
+ {
+ g_warning ("Unable to get session bus: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ error = NULL;
+ watcher->priv->presence_proxy = dbus_g_proxy_new_for_name_owner (bus,
+ "org.mate.SessionManager",
+ "/org/mate/SessionManager/Presence",
+ "org.mate.SessionManager.Presence",
+ &error);
+ if (watcher->priv->presence_proxy != NULL)
+ {
+ DBusGProxy *proxy;
+
+ dbus_g_proxy_add_signal (watcher->priv->presence_proxy,
+ "StatusChanged",
+ G_TYPE_UINT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (watcher->priv->presence_proxy,
+ "StatusChanged",
+ G_CALLBACK (on_presence_status_changed),
+ watcher,
+ NULL);
+ dbus_g_proxy_add_signal (watcher->priv->presence_proxy,
+ "StatusTextChanged",
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (watcher->priv->presence_proxy,
+ "StatusTextChanged",
+ G_CALLBACK (on_presence_status_text_changed),
+ watcher,
+ NULL);
+
+ proxy = dbus_g_proxy_new_from_proxy (watcher->priv->presence_proxy,
+ "org.freedesktop.DBus.Properties",
+ "/org/mate/SessionManager/Presence");
+ if (proxy != NULL)
+ {
+ guint status;
+ const char *status_text;
+ GValue value = { 0, };
+
+ status = 0;
+ status_text = NULL;
+
+ error = NULL;
+ dbus_g_proxy_call (proxy,
+ "Get",
+ &error,
+ G_TYPE_STRING, "org.mate.SessionManager.Presence",
+ G_TYPE_STRING, "status",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID);
+
+ if (error != NULL)
+ {
+ g_warning ("Couldn't get presence status: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+ else
+ {
+ status = g_value_get_uint (&value);
+ }
+
+ g_value_unset (&value);
+
+ error = NULL;
+ dbus_g_proxy_call (proxy,
+ "Get",
+ &error,
+ G_TYPE_STRING, "org.mate.SessionManager.Presence",
+ G_TYPE_STRING, "status-text",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID);
+
+ if (error != NULL)
+ {
+ g_warning ("Couldn't get presence status text: %s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ status_text = g_value_get_string (&value);
+ }
+
+ set_status (watcher, status);
+ set_status_text (watcher, status_text);
+ }
+ }
+ else
+ {
+ g_warning ("Failed to get session presence proxy: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+ return ret;
+}
+
+static void
+gs_watcher_init (GSWatcher *watcher)
+{
+ watcher->priv = GS_WATCHER_GET_PRIVATE (watcher);
+
+ watcher->priv->enabled = TRUE;
+ watcher->priv->active = FALSE;
+
+ connect_presence_watcher (watcher);
+
+ /* time before idle signal to send notice signal */
+ watcher->priv->delta_notice_timeout = 10000;
+
+ add_watchdog_timer (watcher, 600000);
+}
+
+static void
+gs_watcher_finalize (GObject *object)
+{
+ GSWatcher *watcher;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_WATCHER (object));
+
+ watcher = GS_WATCHER (object);
+
+ g_return_if_fail (watcher->priv != NULL);
+
+ remove_watchdog_timer (watcher);
+
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+
+ watcher->priv->active = FALSE;
+
+ if (watcher->priv->presence_proxy != NULL)
+ {
+ g_object_unref (watcher->priv->presence_proxy);
+ }
+
+ g_free (watcher->priv->status_message);
+
+ G_OBJECT_CLASS (gs_watcher_parent_class)->finalize (object);
+}
+
+/* Figuring out what the appropriate XSetScreenSaver() parameters are
+ (one wouldn't expect this to be rocket science.)
+*/
+static void
+disable_builtin_screensaver (GSWatcher *watcher,
+ gboolean unblank_screen)
+{
+ int current_server_timeout, current_server_interval;
+ int current_prefer_blank, current_allow_exp;
+ int desired_server_timeout, desired_server_interval;
+ int desired_prefer_blank, desired_allow_exp;
+
+ XGetScreenSaver (GDK_DISPLAY (),
+ &current_server_timeout,
+ &current_server_interval,
+ &current_prefer_blank,
+ &current_allow_exp);
+
+ desired_server_timeout = current_server_timeout;
+ desired_server_interval = current_server_interval;
+ desired_prefer_blank = current_prefer_blank;
+ desired_allow_exp = current_allow_exp;
+
+ desired_server_interval = 0;
+
+ /* I suspect (but am not sure) that DontAllowExposures might have
+ something to do with powering off the monitor as well, at least
+ on some systems that don't support XDPMS? Who know... */
+ desired_allow_exp = AllowExposures;
+
+ /* When we're not using an extension, set the server-side timeout to 0,
+ so that the server never gets involved with screen blanking, and we
+ do it all ourselves. (However, when we *are* using an extension,
+ we tell the server when to notify us, and rather than blanking the
+ screen, the server will send us an X event telling us to blank.)
+ */
+ desired_server_timeout = 0;
+
+ if (desired_server_timeout != current_server_timeout
+ || desired_server_interval != current_server_interval
+ || desired_prefer_blank != current_prefer_blank
+ || desired_allow_exp != current_allow_exp)
+ {
+
+ gs_debug ("disabling server builtin screensaver:"
+ " (xset s %d %d; xset s %s; xset s %s)",
+ desired_server_timeout,
+ desired_server_interval,
+ (desired_prefer_blank ? "blank" : "noblank"),
+ (desired_allow_exp ? "expose" : "noexpose"));
+
+ XSetScreenSaver (GDK_DISPLAY (),
+ desired_server_timeout,
+ desired_server_interval,
+ desired_prefer_blank,
+ desired_allow_exp);
+
+ XSync (GDK_DISPLAY (), FALSE);
+ }
+
+ if (unblank_screen)
+ {
+ /* Turn off the server builtin saver if it is now running. */
+ XForceScreenSaver (GDK_DISPLAY (), ScreenSaverReset);
+ }
+}
+
+
+/* This timer goes off every few minutes, whether the user is idle or not,
+ to try and clean up anything that has gone wrong.
+
+ It calls disable_builtin_screensaver() so that if xset has been used,
+ or some other program (like xlock) has messed with the XSetScreenSaver()
+ settings, they will be set back to sensible values (if a server extension
+ is in use, messing with xlock can cause the screensaver to never get a wakeup
+ event, and could cause monitor power-saving to occur, and all manner of
+ heinousness.)
+
+ */
+
+static gboolean
+watchdog_timer (GSWatcher *watcher)
+{
+
+ disable_builtin_screensaver (watcher, FALSE);
+
+ return TRUE;
+}
+
+GSWatcher *
+gs_watcher_new (void)
+{
+ GSWatcher *watcher;
+
+ watcher = g_object_new (GS_TYPE_WATCHER,
+ NULL);
+
+ return GS_WATCHER (watcher);
+}
diff --git a/src/gs-watcher-x11.c.orig b/src/gs-watcher-x11.c.orig
new file mode 100644
index 0000000..a9c90e8
--- /dev/null
+++ b/src/gs-watcher-x11.c.orig
@@ -0,0 +1,680 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include <string.h>
+#include <gdk/gdkx.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+
+#include "gs-watcher.h"
+#include "gs-marshal.h"
+#include "gs-debug.h"
+
+static void gs_watcher_class_init (GSWatcherClass *klass);
+static void gs_watcher_init (GSWatcher *watcher);
+static void gs_watcher_finalize (GObject *object);
+
+static gboolean watchdog_timer (GSWatcher *watcher);
+
+#define GS_WATCHER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_WATCHER, GSWatcherPrivate))
+
+struct GSWatcherPrivate
+{
+ /* settings */
+ guint enabled : 1;
+ guint delta_notice_timeout;
+
+ /* state */
+ guint active : 1;
+ guint idle : 1;
+ guint idle_notice : 1;
+
+ guint idle_id;
+ char *status_message;
+
+ DBusGProxy *presence_proxy;
+ guint watchdog_timer_id;
+};
+
+enum
+{
+ PROP_0,
+ PROP_STATUS_MESSAGE
+};
+
+enum
+{
+ IDLE_CHANGED,
+ IDLE_NOTICE_CHANGED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSWatcher, gs_watcher, G_TYPE_OBJECT)
+
+static void
+remove_watchdog_timer (GSWatcher *watcher)
+{
+ if (watcher->priv->watchdog_timer_id != 0)
+ {
+ g_source_remove (watcher->priv->watchdog_timer_id);
+ watcher->priv->watchdog_timer_id = 0;
+ }
+}
+
+static void
+add_watchdog_timer (GSWatcher *watcher,
+ glong timeout)
+{
+ watcher->priv->watchdog_timer_id = g_timeout_add (timeout,
+ (GSourceFunc)watchdog_timer,
+ watcher);
+}
+
+static void
+gs_watcher_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSWatcher *self;
+
+ self = GS_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_status_text (GSWatcher *watcher,
+ const char *text)
+{
+ g_free (watcher->priv->status_message);
+
+ watcher->priv->status_message = g_strdup (text);
+ g_object_notify (G_OBJECT (watcher), "status-message");
+}
+
+static void
+gs_watcher_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSWatcher *self;
+
+ self = GS_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_STATUS_MESSAGE:
+ set_status_text (self, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_watcher_class_init (GSWatcherClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gs_watcher_finalize;
+ object_class->get_property = gs_watcher_get_property;
+ object_class->set_property = gs_watcher_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_STATUS_MESSAGE,
+ g_param_spec_string ("status-message",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ signals [IDLE_CHANGED] =
+ g_signal_new ("idle-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWatcherClass, idle_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1, G_TYPE_BOOLEAN);
+ signals [IDLE_NOTICE_CHANGED] =
+ g_signal_new ("idle-notice-changed",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWatcherClass, idle_notice_changed),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__BOOLEAN,
+ G_TYPE_BOOLEAN,
+ 1, G_TYPE_BOOLEAN);
+
+ g_type_class_add_private (klass, sizeof (GSWatcherPrivate));
+}
+
+static gboolean
+_gs_watcher_set_session_idle_notice (GSWatcher *watcher,
+ gboolean in_effect)
+{
+ gboolean res;
+
+ res = FALSE;
+
+ if (in_effect != watcher->priv->idle_notice)
+ {
+
+ g_signal_emit (watcher, signals [IDLE_NOTICE_CHANGED], 0, in_effect, &res);
+ if (res)
+ {
+ gs_debug ("Changing idle notice state: %d", in_effect);
+
+ watcher->priv->idle_notice = in_effect;
+ }
+ else
+ {
+ gs_debug ("Idle notice signal not handled: %d", in_effect);
+ }
+ }
+
+ return res;
+}
+
+static gboolean
+_gs_watcher_set_session_idle (GSWatcher *watcher,
+ gboolean is_idle)
+{
+ gboolean res;
+
+ res = FALSE;
+
+ if (is_idle != watcher->priv->idle)
+ {
+
+ g_signal_emit (watcher, signals [IDLE_CHANGED], 0, is_idle, &res);
+ if (res)
+ {
+ gs_debug ("Changing idle state: %d", is_idle);
+
+ watcher->priv->idle = is_idle;
+ }
+ else
+ {
+ gs_debug ("Idle changed signal not handled: %d", is_idle);
+ }
+ }
+
+ return res;
+}
+
+gboolean
+gs_watcher_get_active (GSWatcher *watcher)
+{
+ gboolean active;
+
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ active = watcher->priv->active;
+
+ return active;
+}
+
+static void
+_gs_watcher_reset_state (GSWatcher *watcher)
+{
+ watcher->priv->idle = FALSE;
+ watcher->priv->idle_notice = FALSE;
+}
+
+static gboolean
+_gs_watcher_set_active_internal (GSWatcher *watcher,
+ gboolean active)
+{
+ if (active != watcher->priv->active)
+ {
+ /* reset state */
+ _gs_watcher_reset_state (watcher);
+
+ watcher->priv->active = active;
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_watcher_set_active (GSWatcher *watcher,
+ gboolean active)
+{
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ gs_debug ("turning watcher: %s", active ? "ON" : "OFF");
+
+ if (watcher->priv->active == active)
+ {
+ gs_debug ("Idle detection is already %s",
+ active ? "active" : "inactive");
+ return FALSE;
+ }
+
+ if (! watcher->priv->enabled)
+ {
+ gs_debug ("Idle detection is disabled, cannot activate");
+ return FALSE;
+ }
+
+ return _gs_watcher_set_active_internal (watcher, active);
+}
+
+gboolean
+gs_watcher_set_enabled (GSWatcher *watcher,
+ gboolean enabled)
+{
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ if (watcher->priv->enabled != enabled)
+ {
+ gboolean is_active = gs_watcher_get_active (watcher);
+
+ watcher->priv->enabled = enabled;
+
+ /* if we are disabling the watcher and we are
+ active shut it down */
+ if (! enabled && is_active)
+ {
+ _gs_watcher_set_active_internal (watcher, FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+gboolean
+gs_watcher_get_enabled (GSWatcher *watcher)
+{
+ gboolean enabled;
+
+ g_return_val_if_fail (GS_IS_WATCHER (watcher), FALSE);
+
+ enabled = watcher->priv->enabled;
+
+ return enabled;
+}
+
+static gboolean
+on_idle_timeout (GSWatcher *watcher)
+{
+ gboolean res;
+
+ res = _gs_watcher_set_session_idle (watcher, TRUE);
+
+ _gs_watcher_set_session_idle_notice (watcher, FALSE);
+
+ /* try again if we failed i guess */
+ return !res;
+}
+
+static void
+set_status (GSWatcher *watcher,
+ guint status)
+{
+ gboolean res;
+ gboolean is_idle;
+
+ if (! watcher->priv->active)
+ {
+ gs_debug ("GSWatcher: not active, ignoring status changes");
+ return;
+ }
+
+ is_idle = (status == 3);
+
+ if (!is_idle && !watcher->priv->idle_notice)
+ {
+ /* no change in idleness */
+ return;
+ }
+
+ if (is_idle)
+ {
+ res = _gs_watcher_set_session_idle_notice (watcher, is_idle);
+ /* queue an activation */
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+ watcher->priv->idle_id = g_timeout_add (watcher->priv->delta_notice_timeout,
+ (GSourceFunc)on_idle_timeout,
+ watcher);
+ }
+ else
+ {
+ /* cancel notice too */
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+ res = _gs_watcher_set_session_idle (watcher, FALSE);
+ res = _gs_watcher_set_session_idle_notice (watcher, FALSE);
+ }
+}
+
+static void
+on_presence_status_changed (DBusGProxy *presence_proxy,
+ guint status,
+ GSWatcher *watcher)
+{
+ set_status (watcher, status);
+}
+
+static void
+on_presence_status_text_changed (DBusGProxy *presence_proxy,
+ const char *status_text,
+ GSWatcher *watcher)
+{
+ set_status_text (watcher, status_text);
+}
+
+static gboolean
+connect_presence_watcher (GSWatcher *watcher)
+{
+ DBusGConnection *bus;
+ GError *error;
+ gboolean ret;
+
+ ret = FALSE;
+
+ error = NULL;
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ if (bus == NULL)
+ {
+ g_warning ("Unable to get session bus: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ error = NULL;
+ watcher->priv->presence_proxy = dbus_g_proxy_new_for_name_owner (bus,
+ "org.mate.SessionManager",
+ "/org/mate/SessionManager/Presence",
+ "org.mate.SessionManager.Presence",
+ &error);
+ if (watcher->priv->presence_proxy != NULL)
+ {
+ DBusGProxy *proxy;
+
+ dbus_g_proxy_add_signal (watcher->priv->presence_proxy,
+ "StatusChanged",
+ G_TYPE_UINT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (watcher->priv->presence_proxy,
+ "StatusChanged",
+ G_CALLBACK (on_presence_status_changed),
+ watcher,
+ NULL);
+ dbus_g_proxy_add_signal (watcher->priv->presence_proxy,
+ "StatusTextChanged",
+ G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (watcher->priv->presence_proxy,
+ "StatusTextChanged",
+ G_CALLBACK (on_presence_status_text_changed),
+ watcher,
+ NULL);
+
+ proxy = dbus_g_proxy_new_from_proxy (watcher->priv->presence_proxy,
+ "org.freedesktop.DBus.Properties",
+ "/org/mate/SessionManager/Presence");
+ if (proxy != NULL)
+ {
+ guint status;
+ const char *status_text;
+ GValue value = { 0, };
+
+ status = 0;
+ status_text = NULL;
+
+ error = NULL;
+ dbus_g_proxy_call (proxy,
+ "Get",
+ &error,
+ G_TYPE_STRING, "org.mate.SessionManager.Presence",
+ G_TYPE_STRING, "status",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID);
+
+ if (error != NULL)
+ {
+ g_warning ("Couldn't get presence status: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+ else
+ {
+ status = g_value_get_uint (&value);
+ }
+
+ g_value_unset (&value);
+
+ error = NULL;
+ dbus_g_proxy_call (proxy,
+ "Get",
+ &error,
+ G_TYPE_STRING, "org.mate.SessionManager.Presence",
+ G_TYPE_STRING, "status-text",
+ G_TYPE_INVALID,
+ G_TYPE_VALUE, &value,
+ G_TYPE_INVALID);
+
+ if (error != NULL)
+ {
+ g_warning ("Couldn't get presence status text: %s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ status_text = g_value_get_string (&value);
+ }
+
+ set_status (watcher, status);
+ set_status_text (watcher, status_text);
+ }
+ }
+ else
+ {
+ g_warning ("Failed to get session presence proxy: %s", error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ ret = TRUE;
+
+done:
+ return ret;
+}
+
+static void
+gs_watcher_init (GSWatcher *watcher)
+{
+ watcher->priv = GS_WATCHER_GET_PRIVATE (watcher);
+
+ watcher->priv->enabled = TRUE;
+ watcher->priv->active = FALSE;
+
+ connect_presence_watcher (watcher);
+
+ /* time before idle signal to send notice signal */
+ watcher->priv->delta_notice_timeout = 10000;
+
+ add_watchdog_timer (watcher, 600000);
+}
+
+static void
+gs_watcher_finalize (GObject *object)
+{
+ GSWatcher *watcher;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_WATCHER (object));
+
+ watcher = GS_WATCHER (object);
+
+ g_return_if_fail (watcher->priv != NULL);
+
+ remove_watchdog_timer (watcher);
+
+ if (watcher->priv->idle_id > 0)
+ {
+ g_source_remove (watcher->priv->idle_id);
+ }
+
+ watcher->priv->active = FALSE;
+
+ if (watcher->priv->presence_proxy != NULL)
+ {
+ g_object_unref (watcher->priv->presence_proxy);
+ }
+
+ g_free (watcher->priv->status_message);
+
+ G_OBJECT_CLASS (gs_watcher_parent_class)->finalize (object);
+}
+
+/* Figuring out what the appropriate XSetScreenSaver() parameters are
+ (one wouldn't expect this to be rocket science.)
+*/
+static void
+disable_builtin_screensaver (GSWatcher *watcher,
+ gboolean unblank_screen)
+{
+ int current_server_timeout, current_server_interval;
+ int current_prefer_blank, current_allow_exp;
+ int desired_server_timeout, desired_server_interval;
+ int desired_prefer_blank, desired_allow_exp;
+
+ XGetScreenSaver (GDK_DISPLAY (),
+ &current_server_timeout,
+ &current_server_interval,
+ &current_prefer_blank,
+ &current_allow_exp);
+
+ desired_server_timeout = current_server_timeout;
+ desired_server_interval = current_server_interval;
+ desired_prefer_blank = current_prefer_blank;
+ desired_allow_exp = current_allow_exp;
+
+ desired_server_interval = 0;
+
+ /* I suspect (but am not sure) that DontAllowExposures might have
+ something to do with powering off the monitor as well, at least
+ on some systems that don't support XDPMS? Who know... */
+ desired_allow_exp = AllowExposures;
+
+ /* When we're not using an extension, set the server-side timeout to 0,
+ so that the server never gets involved with screen blanking, and we
+ do it all ourselves. (However, when we *are* using an extension,
+ we tell the server when to notify us, and rather than blanking the
+ screen, the server will send us an X event telling us to blank.)
+ */
+ desired_server_timeout = 0;
+
+ if (desired_server_timeout != current_server_timeout
+ || desired_server_interval != current_server_interval
+ || desired_prefer_blank != current_prefer_blank
+ || desired_allow_exp != current_allow_exp)
+ {
+
+ gs_debug ("disabling server builtin screensaver:"
+ " (xset s %d %d; xset s %s; xset s %s)",
+ desired_server_timeout,
+ desired_server_interval,
+ (desired_prefer_blank ? "blank" : "noblank"),
+ (desired_allow_exp ? "expose" : "noexpose"));
+
+ XSetScreenSaver (GDK_DISPLAY (),
+ desired_server_timeout,
+ desired_server_interval,
+ desired_prefer_blank,
+ desired_allow_exp);
+
+ XSync (GDK_DISPLAY (), FALSE);
+ }
+
+ if (unblank_screen)
+ {
+ /* Turn off the server builtin saver if it is now running. */
+ XForceScreenSaver (GDK_DISPLAY (), ScreenSaverReset);
+ }
+}
+
+
+/* This timer goes off every few minutes, whether the user is idle or not,
+ to try and clean up anything that has gone wrong.
+
+ It calls disable_builtin_screensaver() so that if xset has been used,
+ or some other program (like xlock) has messed with the XSetScreenSaver()
+ settings, they will be set back to sensible values (if a server extension
+ is in use, messing with xlock can cause the screensaver to never get a wakeup
+ event, and could cause monitor power-saving to occur, and all manner of
+ heinousness.)
+
+ */
+
+static gboolean
+watchdog_timer (GSWatcher *watcher)
+{
+
+ disable_builtin_screensaver (watcher, FALSE);
+
+ return TRUE;
+}
+
+GSWatcher *
+gs_watcher_new (void)
+{
+ GSWatcher *watcher;
+
+ watcher = g_object_new (GS_TYPE_WATCHER,
+ NULL);
+
+ return GS_WATCHER (watcher);
+}
diff --git a/src/gs-watcher.h b/src/gs-watcher.h
new file mode 100644
index 0000000..be06bc7
--- /dev/null
+++ b/src/gs-watcher.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_WATCHER_H
+#define __GS_WATCHER_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_WATCHER (gs_watcher_get_type ())
+#define GS_WATCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_WATCHER, GSWatcher))
+#define GS_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_WATCHER, GSWatcherClass))
+#define GS_IS_WATCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_WATCHER))
+#define GS_IS_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_WATCHER))
+#define GS_WATCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_WATCHER, GSWatcherClass))
+
+typedef struct GSWatcherPrivate GSWatcherPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSWatcherPrivate *priv;
+} GSWatcher;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ gboolean (* idle_changed) (GSWatcher *watcher,
+ gboolean is_idle);
+ gboolean (* idle_notice_changed) (GSWatcher *watcher,
+ gboolean in_effect);
+} GSWatcherClass;
+
+GType gs_watcher_get_type (void);
+
+GSWatcher * gs_watcher_new (void);
+gboolean gs_watcher_set_enabled (GSWatcher *watcher,
+ gboolean enabled);
+gboolean gs_watcher_get_enabled (GSWatcher *watcher);
+gboolean gs_watcher_set_active (GSWatcher *watcher,
+ gboolean active);
+gboolean gs_watcher_get_active (GSWatcher *watcher);
+
+G_END_DECLS
+
+#endif /* __GS_WATCHER_H */
diff --git a/src/gs-watcher.h.orig b/src/gs-watcher.h.orig
new file mode 100644
index 0000000..cc42dea
--- /dev/null
+++ b/src/gs-watcher.h.orig
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_WATCHER_H
+#define __GS_WATCHER_H
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_WATCHER (gs_watcher_get_type ())
+#define GS_WATCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_WATCHER, GSWatcher))
+#define GS_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_WATCHER, GSWatcherClass))
+#define GS_IS_WATCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_WATCHER))
+#define GS_IS_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_WATCHER))
+#define GS_WATCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_WATCHER, GSWatcherClass))
+
+typedef struct GSWatcherPrivate GSWatcherPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GSWatcherPrivate *priv;
+} GSWatcher;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ gboolean (* idle_changed) (GSWatcher *watcher,
+ gboolean is_idle);
+ gboolean (* idle_notice_changed) (GSWatcher *watcher,
+ gboolean in_effect);
+} GSWatcherClass;
+
+GType gs_watcher_get_type (void);
+
+GSWatcher * gs_watcher_new (void);
+gboolean gs_watcher_set_enabled (GSWatcher *watcher,
+ gboolean enabled);
+gboolean gs_watcher_get_enabled (GSWatcher *watcher);
+gboolean gs_watcher_set_active (GSWatcher *watcher,
+ gboolean active);
+gboolean gs_watcher_get_active (GSWatcher *watcher);
+
+G_END_DECLS
+
+#endif /* __GS_WATCHER_H */
diff --git a/src/gs-window-x11.c b/src/gs-window-x11.c
new file mode 100644
index 0000000..2e6f0f7
--- /dev/null
+++ b/src/gs-window-x11.c
@@ -0,0 +1,2647 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2008 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gs-window.h"
+#include "gs-marshal.h"
+#include "subprocs.h"
+#include "gs-debug.h"
+
+#ifdef HAVE_SHAPE_EXT
+#include <X11/extensions/shape.h>
+#endif
+
+static void gs_window_class_init (GSWindowClass *klass);
+static void gs_window_init (GSWindow *window);
+static void gs_window_finalize (GObject *object);
+
+static gboolean popup_dialog_idle (GSWindow *window);
+static void gs_window_dialog_finish (GSWindow *window);
+static void remove_command_watches (GSWindow *window);
+
+enum
+{
+ DIALOG_RESPONSE_CANCEL,
+ DIALOG_RESPONSE_OK
+};
+
+#define MAX_QUEUED_EVENTS 16
+#define INFO_BAR_SECONDS 30
+
+#define GS_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_WINDOW, GSWindowPrivate))
+
+struct GSWindowPrivate
+{
+ int monitor;
+
+ GdkRectangle geometry;
+ guint obscured : 1;
+ guint dialog_up : 1;
+
+ guint lock_enabled : 1;
+ guint user_switch_enabled : 1;
+ guint logout_enabled : 1;
+ guint keyboard_enabled : 1;
+
+ guint64 logout_timeout;
+ char *logout_command;
+ char *keyboard_command;
+ char *status_message;
+
+ GtkWidget *vbox;
+ GtkWidget *drawing_area;
+ GtkWidget *lock_box;
+ GtkWidget *lock_socket;
+ GtkWidget *keyboard_socket;
+ GtkWidget *info_bar;
+ GtkWidget *info_content;
+
+ GdkPixmap *background_pixmap;
+
+ guint popup_dialog_idle_id;
+
+ guint dialog_map_signal_id;
+ guint dialog_unmap_signal_id;
+ guint dialog_response_signal_id;
+
+ guint watchdog_timer_id;
+ guint info_bar_timer_id;
+
+ gint lock_pid;
+ gint lock_watch_id;
+ gint dialog_response;
+ gboolean dialog_quit_requested;
+ gboolean dialog_shake_in_progress;
+
+ gint keyboard_pid;
+ gint keyboard_watch_id;
+
+ GList *key_events;
+
+ gdouble last_x;
+ gdouble last_y;
+
+ GTimer *timer;
+
+#ifdef HAVE_SHAPE_EXT
+ int shape_event_base;
+#endif
+};
+
+enum
+{
+ ACTIVITY,
+ DEACTIVATED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_OBSCURED,
+ PROP_DIALOG_UP,
+ PROP_LOCK_ENABLED,
+ PROP_LOGOUT_ENABLED,
+ PROP_KEYBOARD_ENABLED,
+ PROP_KEYBOARD_COMMAND,
+ PROP_LOGOUT_COMMAND,
+ PROP_LOGOUT_TIMEOUT,
+ PROP_MONITOR,
+ PROP_STATUS_MESSAGE
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSWindow, gs_window, GTK_TYPE_WINDOW)
+
+static void
+set_invisible_cursor (GdkWindow *window,
+ gboolean invisible)
+{
+ GdkBitmap *empty_bitmap;
+ GdkCursor *cursor = NULL;
+ GdkColor useless;
+ char invisible_cursor_bits [] = { 0x0 };
+
+ if (invisible)
+ {
+ useless.red = useless.green = useless.blue = 0;
+ useless.pixel = 0;
+
+ empty_bitmap = gdk_bitmap_create_from_data (window,
+ invisible_cursor_bits,
+ 1, 1);
+
+ cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
+ empty_bitmap,
+ &useless,
+ &useless, 0, 0);
+
+ g_object_unref (empty_bitmap);
+ }
+
+ gdk_window_set_cursor (window, cursor);
+
+ if (cursor)
+ {
+ gdk_cursor_unref (cursor);
+ }
+}
+
+/* derived from tomboy */
+static void
+gs_window_override_user_time (GSWindow *window)
+{
+ guint32 ev_time = gtk_get_current_event_time ();
+
+ if (ev_time == 0)
+ {
+ gint ev_mask = gtk_widget_get_events (GTK_WIDGET (window));
+ if (!(ev_mask & GDK_PROPERTY_CHANGE_MASK))
+ {
+ gtk_widget_add_events (GTK_WIDGET (window),
+ GDK_PROPERTY_CHANGE_MASK);
+ }
+
+ /*
+ * NOTE: Last resort for D-BUS or other non-interactive
+ * openings. Causes roundtrip to server. Lame.
+ */
+ ev_time = gdk_x11_get_server_time (GTK_WIDGET (window)->window);
+ }
+
+ gdk_x11_window_set_user_time (GTK_WIDGET (window)->window, ev_time);
+}
+
+static void
+force_no_pixmap_background (GtkWidget *widget)
+{
+ static gboolean first_time = TRUE;
+
+ if (first_time)
+ {
+ gtk_rc_parse_string ("\n"
+ " style \"gs-theme-engine-style\"\n"
+ " {\n"
+ " bg_pixmap[NORMAL] = \"<none>\"\n"
+ " bg_pixmap[INSENSITIVE] = \"<none>\"\n"
+ " bg_pixmap[ACTIVE] = \"<none>\"\n"
+ " bg_pixmap[PRELIGHT] = \"<none>\"\n"
+ " bg[NORMAL] = { 0.0, 0.0, 0.0 }\n"
+ " bg[INSENSITIVE] = { 0.0, 0.0, 0.0 }\n"
+ " bg[ACTIVE] = { 0.0, 0.0, 0.0 }\n"
+ " bg[PRELIGHT] = { 0.0, 0.0, 0.0 }\n"
+ " }\n"
+ " widget \"gs-window-drawing-area*\" style : highest \"gs-theme-engine-style\"\n"
+ "\n");
+ first_time = FALSE;
+ }
+
+ gtk_widget_set_name (widget, "gs-window-drawing-area");
+}
+
+static void
+clear_children (Window window)
+{
+ Window root;
+ Window parent;
+ Window *children;
+ unsigned int n_children;
+ int status;
+
+ children = NULL;
+ status = XQueryTree (GDK_DISPLAY (), window, &root, &parent, &children, &n_children);
+
+ if (status == 0)
+ {
+ if (children)
+ {
+ XFree (children);
+ }
+ return;
+ }
+
+ if (children)
+ {
+ while (n_children)
+ {
+ Window child;
+
+ child = children [--n_children];
+
+ XClearWindow (GDK_DISPLAY (), child);
+ clear_children (child);
+ }
+
+ XFree (children);
+ }
+}
+
+static void
+widget_clear_all_children (GtkWidget *widget)
+{
+ GdkWindow *w;
+
+ gs_debug ("Clearing all child windows");
+
+ gdk_error_trap_push ();
+
+ w = widget->window;
+
+ clear_children (GDK_WINDOW_XID (w));
+
+ gdk_display_sync (gtk_widget_get_display (widget));
+ gdk_error_trap_pop ();
+}
+
+void
+gs_window_set_background_pixmap (GSWindow *window,
+ GdkPixmap *pixmap)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->background_pixmap != NULL)
+ {
+ g_object_unref (window->priv->background_pixmap);
+ }
+
+ if (pixmap != NULL)
+ {
+ window->priv->background_pixmap = g_object_ref (pixmap);
+ gdk_window_set_back_pixmap (GTK_WIDGET (window)->window,
+ pixmap,
+ FALSE);
+ }
+}
+
+static void
+gs_window_clear_to_background_pixmap (GSWindow *window)
+{
+ GtkStateType state;
+ GtkStyle *style;
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (window)))
+ {
+ return;
+ }
+
+ if (window->priv->background_pixmap == NULL)
+ {
+ /* don't allow null pixmaps */
+ return;
+ }
+
+ gs_debug ("Clearing window to background pixmap");
+
+ style = gtk_style_copy (GTK_WIDGET (window)->style);
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (GTK_WIDGET (window)->style->bg_pixmap))
+ {
+
+ if (style->bg_pixmap[state] != NULL)
+ {
+ g_object_unref (style->bg_pixmap[state]);
+ }
+
+ style->bg_pixmap[state] = g_object_ref (window->priv->background_pixmap);
+ state++;
+ }
+
+ gtk_widget_set_style (GTK_WIDGET (window), style);
+ g_object_unref (style);
+
+ if (window->priv->background_pixmap != NULL)
+ {
+ gdk_window_set_back_pixmap (GTK_WIDGET (window)->window,
+ window->priv->background_pixmap,
+ FALSE);
+ }
+
+ gdk_window_clear (GTK_WIDGET (window)->window);
+
+ gdk_flush ();
+}
+
+static void
+clear_widget (GtkWidget *widget)
+{
+ GdkColor color = { 0, 0x0000, 0x0000, 0x0000 };
+ GdkColormap *colormap;
+ GtkStateType state;
+ GtkStyle *style;
+
+ if (! GTK_WIDGET_VISIBLE (widget))
+ {
+ return;
+ }
+
+ gs_debug ("Clearing widget");
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg))
+ {
+ gtk_widget_modify_bg (widget, state, &color);
+ state++;
+ }
+
+ style = gtk_style_copy (widget->style);
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg_pixmap))
+ {
+
+ if (style->bg_pixmap[state] != NULL)
+ {
+ g_object_unref (style->bg_pixmap[state]);
+ }
+
+ style->bg_pixmap[state] = NULL;
+ state++;
+ }
+
+ colormap = gdk_drawable_get_colormap (widget->window);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_window_set_background (widget->window, &color);
+
+ gtk_widget_set_style (widget, style);
+ g_object_unref (style);
+
+ gdk_window_clear (widget->window);
+
+ /* If a screensaver theme adds child windows we need to clear them too */
+ widget_clear_all_children (widget);
+
+ gdk_flush ();
+}
+
+void
+gs_window_clear (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ clear_widget (GTK_WIDGET (window));
+ clear_widget (window->priv->drawing_area);
+}
+
+static GdkRegion *
+get_outside_region (GSWindow *window)
+{
+ int i;
+ GdkRegion *region;
+
+ region = gdk_region_new ();
+ for (i = 0; i < window->priv->monitor; i++)
+ {
+ GdkRectangle geometry;
+
+ gdk_screen_get_monitor_geometry (GTK_WINDOW (window)->screen,
+ i, &geometry);
+ gdk_region_union_with_rect (region, &geometry);
+ }
+
+ return region;
+}
+
+static void
+update_geometry (GSWindow *window)
+{
+ GdkRectangle geometry;
+ GdkRegion *outside_region;
+ GdkRegion *monitor_region;
+
+ outside_region = get_outside_region (window);
+
+ gdk_screen_get_monitor_geometry (GTK_WINDOW (window)->screen,
+ window->priv->monitor,
+ &geometry);
+ gs_debug ("got geometry for monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ geometry.x,
+ geometry.y,
+ geometry.width,
+ geometry.height);
+ monitor_region = gdk_region_rectangle (&geometry);
+ gdk_region_subtract (monitor_region, outside_region);
+ gdk_region_destroy (outside_region);
+
+ gdk_region_get_clipbox (monitor_region, &geometry);
+ gdk_region_destroy (monitor_region);
+
+ gs_debug ("using geometry for monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ geometry.x,
+ geometry.y,
+ geometry.width,
+ geometry.height);
+
+ window->priv->geometry.x = geometry.x;
+ window->priv->geometry.y = geometry.y;
+ window->priv->geometry.width = geometry.width;
+ window->priv->geometry.height = geometry.height;
+}
+
+static void
+screen_size_changed (GdkScreen *screen,
+ GSWindow *window)
+{
+ gs_debug ("Got screen size changed signal");
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+}
+
+/* copied from panel-toplevel.c */
+static void
+gs_window_move_resize_window (GSWindow *window,
+ gboolean move,
+ gboolean resize)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (window);
+
+ g_assert (GTK_WIDGET_REALIZED (widget));
+
+ gs_debug ("Move and/or resize window on monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ window->priv->geometry.x,
+ window->priv->geometry.y,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+
+ if (move && resize)
+ {
+ gdk_window_move_resize (widget->window,
+ window->priv->geometry.x,
+ window->priv->geometry.y,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+ }
+ else if (move)
+ {
+ gdk_window_move (widget->window,
+ window->priv->geometry.x,
+ window->priv->geometry.y);
+ }
+ else if (resize)
+ {
+ gdk_window_resize (widget->window,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+ }
+}
+
+static void
+gs_window_real_unrealize (GtkWidget *widget)
+{
+ g_signal_handlers_disconnect_by_func (gtk_window_get_screen (GTK_WINDOW (widget)),
+ screen_size_changed,
+ widget);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->unrealize)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->unrealize (widget);
+ }
+}
+
+/* copied from gdk */
+extern char **environ;
+
+static gchar **
+spawn_make_environment_for_screen (GdkScreen *screen,
+ gchar **envp)
+{
+ gchar **retval = NULL;
+ gchar *display_name;
+ gint display_index = -1;
+ gint i, env_len;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ if (envp == NULL)
+ envp = environ;
+
+ for (env_len = 0; envp[env_len]; env_len++)
+ if (strncmp (envp[env_len], "DISPLAY", strlen ("DISPLAY")) == 0)
+ display_index = env_len;
+
+ retval = g_new (char *, env_len + 1);
+ retval[env_len] = NULL;
+
+ display_name = gdk_screen_make_display_name (screen);
+
+ for (i = 0; i < env_len; i++)
+ if (i == display_index)
+ retval[i] = g_strconcat ("DISPLAY=", display_name, NULL);
+ else
+ retval[i] = g_strdup (envp[i]);
+
+ g_assert (i == env_len);
+
+ g_free (display_name);
+
+ return retval;
+}
+
+static gboolean
+spawn_command_line_on_screen_sync (GdkScreen *screen,
+ const gchar *command_line,
+ char **standard_output,
+ char **standard_error,
+ int *exit_status,
+ GError **error)
+{
+ char **argv = NULL;
+ char **envp = NULL;
+ gboolean retval;
+
+ g_return_val_if_fail (command_line != NULL, FALSE);
+
+ if (! g_shell_parse_argv (command_line, NULL, &argv, error))
+ {
+ return FALSE;
+ }
+
+ envp = spawn_make_environment_for_screen (screen, NULL);
+
+ retval = g_spawn_sync (NULL,
+ argv,
+ envp,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ standard_output,
+ standard_error,
+ exit_status,
+ error);
+
+ g_strfreev (argv);
+ g_strfreev (envp);
+
+ return retval;
+}
+
+static GdkVisual *
+get_best_visual_for_screen (GdkScreen *screen)
+{
+ char *command;
+ char *std_output;
+ int exit_status;
+ GError *error;
+ unsigned long v;
+ char c;
+ GdkVisual *visual;
+ gboolean res;
+
+ visual = NULL;
+
+ command = g_build_filename (LIBEXECDIR, "mate-screensaver-gl-helper", NULL);
+
+ error = NULL;
+ std_output = NULL;
+ res = spawn_command_line_on_screen_sync (screen,
+ command,
+ &std_output,
+ NULL,
+ &exit_status,
+ &error);
+ if (! res)
+ {
+ gs_debug ("Could not run command '%s': %s", command, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (1 == sscanf (std_output, "0x%lx %c", &v, &c))
+ {
+ if (v != 0)
+ {
+ VisualID visual_id;
+
+ visual_id = (VisualID) v;
+ visual = gdkx_visual_get (visual_id);
+
+ gs_debug ("Found best GL visual for screen %d: 0x%x",
+ gdk_screen_get_number (screen),
+ (unsigned int) visual_id);
+ }
+ }
+out:
+ g_free (std_output);
+ g_free (command);
+
+ return visual;
+}
+
+static GdkColormap *
+get_best_colormap_for_screen (GdkScreen *screen)
+{
+ GdkColormap *colormap;
+ GdkVisual *visual;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ visual = get_best_visual_for_screen (screen);
+
+ colormap = NULL;
+ if (visual != NULL)
+ {
+ colormap = gdk_colormap_new (visual, FALSE);
+ }
+
+ return colormap;
+}
+
+static void
+widget_set_best_colormap (GtkWidget *widget)
+{
+ GdkColormap *colormap;
+
+ g_return_if_fail (widget != NULL);
+
+ colormap = get_best_colormap_for_screen (gtk_widget_get_screen (widget));
+ if (colormap != NULL)
+ {
+ gtk_widget_set_colormap (widget, colormap);
+ g_object_unref (colormap);
+ }
+}
+
+static void
+gs_window_real_realize (GtkWidget *widget)
+{
+ widget_set_best_colormap (widget);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->realize)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->realize (widget);
+ }
+
+ gs_window_override_user_time (GS_WINDOW (widget));
+
+ gs_window_move_resize_window (GS_WINDOW (widget), TRUE, TRUE);
+
+ g_signal_connect (gtk_window_get_screen (GTK_WINDOW (widget)),
+ "size_changed",
+ G_CALLBACK (screen_size_changed),
+ widget);
+}
+
+/* every so often we should raise the window in case
+ another window has somehow gotten on top */
+static gboolean
+watchdog_timer (GSWindow *window)
+{
+ GtkWidget *widget = GTK_WIDGET (window);
+
+ gdk_window_focus (widget->window, GDK_CURRENT_TIME);
+
+ return TRUE;
+}
+
+static void
+remove_watchdog_timer (GSWindow *window)
+{
+ if (window->priv->watchdog_timer_id != 0)
+ {
+ g_source_remove (window->priv->watchdog_timer_id);
+ window->priv->watchdog_timer_id = 0;
+ }
+}
+
+static void
+add_watchdog_timer (GSWindow *window,
+ glong timeout)
+{
+ window->priv->watchdog_timer_id = g_timeout_add (timeout,
+ (GSourceFunc)watchdog_timer,
+ window);
+}
+
+static void
+remove_popup_dialog_idle (GSWindow *window)
+{
+ if (window->priv->popup_dialog_idle_id != 0)
+ {
+ g_source_remove (window->priv->popup_dialog_idle_id);
+ window->priv->popup_dialog_idle_id = 0;
+ }
+}
+
+static void
+add_popup_dialog_idle (GSWindow *window)
+{
+ window->priv->popup_dialog_idle_id = g_idle_add ((GSourceFunc)popup_dialog_idle, window);
+}
+
+static gboolean
+emit_deactivated_idle (GSWindow *window)
+{
+ g_signal_emit (window, signals [DEACTIVATED], 0);
+
+ return FALSE;
+}
+
+static void
+add_emit_deactivated_idle (GSWindow *window)
+{
+ g_idle_add ((GSourceFunc)emit_deactivated_idle, window);
+}
+
+static void
+gs_window_raise (GSWindow *window)
+{
+ GdkWindow *win;
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Raising screensaver window");
+
+ win = GTK_WIDGET (window)->window;
+
+ gdk_window_raise (win);
+}
+
+static gboolean
+x11_window_is_ours (Window window)
+{
+ GdkWindow *gwindow;
+ gboolean ret;
+
+ ret = FALSE;
+
+ gwindow = gdk_window_lookup (window);
+ if (gwindow && (window != GDK_ROOT_WINDOW ()))
+ {
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+#ifdef HAVE_SHAPE_EXT
+static void
+unshape_window (GSWindow *window)
+{
+ gdk_window_shape_combine_region (GTK_WIDGET (window)->window,
+ NULL,
+ 0,
+ 0);
+}
+#endif
+
+static void
+gs_window_xevent (GSWindow *window,
+ GdkXEvent *xevent)
+{
+ XEvent *ev;
+
+ ev = xevent;
+
+ /* MapNotify is used to tell us when new windows are mapped.
+ ConfigureNofify is used to tell us when windows are raised. */
+ switch (ev->xany.type)
+ {
+ case MapNotify:
+ {
+ XMapEvent *xme = &ev->xmap;
+
+ if (! x11_window_is_ours (xme->window))
+ {
+ gs_window_raise (window);
+ }
+ else
+ {
+ gs_debug ("not raising our windows");
+ }
+
+ break;
+ }
+ case ConfigureNotify:
+ {
+ XConfigureEvent *xce = &ev->xconfigure;
+
+ if (! x11_window_is_ours (xce->window))
+ {
+ gs_window_raise (window);
+ }
+ else
+ {
+ gs_debug ("not raising our windows");
+ }
+
+ break;
+ }
+ default:
+ /* extension events */
+#ifdef HAVE_SHAPE_EXT
+ if (ev->xany.type == (window->priv->shape_event_base + ShapeNotify))
+ {
+ /*XShapeEvent *xse = (XShapeEvent *) ev;*/
+ unshape_window (window);
+ gs_debug ("Window was reshaped!");
+ }
+#endif
+
+ break;
+ }
+
+}
+
+static GdkFilterReturn
+xevent_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ GSWindow *window)
+{
+ gs_window_xevent (window, xevent);
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+select_popup_events (void)
+{
+ XWindowAttributes attr;
+ unsigned long events;
+
+ gdk_error_trap_push ();
+
+ memset (&attr, 0, sizeof (attr));
+ XGetWindowAttributes (GDK_DISPLAY (), GDK_ROOT_WINDOW (), &attr);
+
+ events = SubstructureNotifyMask | attr.your_event_mask;
+ XSelectInput (GDK_DISPLAY (), GDK_ROOT_WINDOW (), events);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+}
+
+static void
+window_select_shape_events (GSWindow *window)
+{
+#ifdef HAVE_SHAPE_EXT
+ unsigned long events;
+ int shape_error_base;
+
+ gdk_error_trap_push ();
+
+ if (XShapeQueryExtension (GDK_DISPLAY (), &window->priv->shape_event_base, &shape_error_base))
+ {
+ events = ShapeNotifyMask;
+ XShapeSelectInput (GDK_DISPLAY (), GDK_WINDOW_XID (GTK_WIDGET (window)->window), events);
+ }
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+#endif
+}
+
+static void
+gs_window_real_show (GtkWidget *widget)
+{
+ GSWindow *window;
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->show)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->show (widget);
+ }
+
+ gs_window_clear (GS_WINDOW (widget));
+
+ set_invisible_cursor (widget->window, TRUE);
+
+ window = GS_WINDOW (widget);
+ if (window->priv->timer)
+ {
+ g_timer_destroy (window->priv->timer);
+ }
+ window->priv->timer = g_timer_new ();
+
+ remove_watchdog_timer (window);
+ add_watchdog_timer (window, 30000);
+
+ select_popup_events ();
+ window_select_shape_events (window);
+ gdk_window_add_filter (NULL, (GdkFilterFunc)xevent_filter, window);
+}
+
+static void
+set_info_text_and_icon (GSWindow *window,
+ const char *icon_stock_id,
+ const char *primary_text,
+ const char *secondary_text)
+{
+ GtkWidget *content_area;
+ GtkWidget *hbox_content;
+ GtkWidget *image;
+ GtkWidget *vbox;
+ gchar *primary_markup;
+ gchar *secondary_markup;
+ GtkWidget *primary_label;
+ GtkWidget *secondary_label;
+
+ hbox_content = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox_content);
+
+ image = gtk_image_new_from_stock (icon_stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_widget_show (image);
+ gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_box_pack_start (GTK_BOX (hbox_content), vbox, FALSE, FALSE, 0);
+
+ primary_markup = g_strdup_printf ("<b>%s</b>", primary_text);
+ primary_label = gtk_label_new (primary_markup);
+ g_free (primary_markup);
+ gtk_widget_show (primary_label);
+ gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
+
+ if (secondary_text != NULL)
+ {
+ secondary_markup = g_strdup_printf ("<small>%s</small>",
+ secondary_text);
+ secondary_label = gtk_label_new (secondary_markup);
+ g_free (secondary_markup);
+ gtk_widget_show (secondary_label);
+ gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5);
+ }
+
+ /* remove old content */
+ content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (window->priv->info_bar));
+ if (window->priv->info_content != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (content_area), window->priv->info_content);
+ }
+ gtk_box_pack_start (GTK_BOX (content_area),
+ hbox_content,
+ TRUE, FALSE, 0);
+ window->priv->info_content = hbox_content;
+}
+
+static gboolean
+info_bar_timeout (GSWindow *window)
+{
+ window->priv->info_bar_timer_id = 0;
+ gtk_widget_hide (window->priv->info_bar);
+ return FALSE;
+}
+
+void
+gs_window_show_message (GSWindow *window,
+ const char *summary,
+ const char *body,
+ const char *icon)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ set_info_text_and_icon (window,
+ icon,
+ summary,
+ body);
+ gtk_widget_show (window->priv->info_bar);
+
+ if (window->priv->info_bar_timer_id > 0)
+ {
+ g_source_remove (window->priv->info_bar_timer_id);
+ }
+
+ window->priv->info_bar_timer_id = g_timeout_add_seconds (INFO_BAR_SECONDS,
+ (GSourceFunc)info_bar_timeout,
+ window);
+}
+
+void
+gs_window_show (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gtk_widget_show (GTK_WIDGET (window));
+}
+
+static void
+gs_window_real_hide (GtkWidget *widget)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+
+ gdk_window_remove_filter (NULL, (GdkFilterFunc)xevent_filter, window);
+
+ remove_watchdog_timer (window);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->hide)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->hide (widget);
+ }
+}
+
+void
+gs_window_destroy (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_window_cancel_unlock_request (window);
+
+ gtk_widget_destroy (GTK_WIDGET (window));
+}
+
+GdkWindow *
+gs_window_get_gdk_window (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return GTK_WIDGET (window)->window;
+}
+
+GtkWidget *
+gs_window_get_drawing_area (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return window->priv->drawing_area;
+}
+
+/* just for debugging */
+static gboolean
+error_watch (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ gboolean finished = FALSE;
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ gs_debug ("command error output: %s", line);
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+spawn_on_window (GSWindow *window,
+ char *command,
+ int *pid,
+ GIOFunc watch_func,
+ gpointer user_data,
+ gint *watch_id)
+{
+ int argc;
+ char **argv;
+ GError *error;
+ gboolean result;
+ GIOChannel *channel;
+ int standard_output;
+ int standard_error;
+ int child_pid;
+ int id;
+
+ error = NULL;
+ if (! g_shell_parse_argv (command, &argc, &argv, &error))
+ {
+ gs_debug ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ error = NULL;
+ result = gdk_spawn_on_screen_with_pipes (GTK_WINDOW (window)->screen,
+ NULL,
+ argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ &child_pid,
+ NULL,
+ &standard_output,
+ &standard_error,
+ &error);
+
+ if (! result)
+ {
+ gs_debug ("Could not start command '%s': %s", command, error->message);
+ g_error_free (error);
+ g_strfreev (argv);
+ return FALSE;
+ }
+
+ if (pid != NULL)
+ {
+ *pid = child_pid;
+ }
+ else
+ {
+ g_spawn_close_pid (child_pid);
+ }
+
+ /* output channel */
+ channel = g_io_channel_unix_new (standard_output);
+ 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);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ watch_func,
+ user_data);
+ if (watch_id != NULL)
+ {
+ *watch_id = id;
+ }
+ g_io_channel_unref (channel);
+
+ /* error channel */
+ channel = g_io_channel_unix_new (standard_error);
+ 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);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ error_watch,
+ NULL);
+ g_io_channel_unref (channel);
+
+ g_strfreev (argv);
+
+ return result;
+}
+
+static void
+lock_plug_added (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_show (widget);
+}
+
+static gboolean
+lock_plug_removed (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_hide (widget);
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->lock_box));
+ window->priv->lock_box = NULL;
+
+ return TRUE;
+}
+
+static void
+keyboard_plug_added (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_show (widget);
+}
+
+static gboolean
+keyboard_plug_removed (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_hide (widget);
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->keyboard_socket));
+
+ return TRUE;
+}
+
+static void
+keyboard_socket_destroyed (GtkWidget *widget,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (widget, keyboard_socket_destroyed, window);
+ g_signal_handlers_disconnect_by_func (widget, keyboard_plug_added, window);
+ g_signal_handlers_disconnect_by_func (widget, keyboard_plug_removed, window);
+
+ window->priv->keyboard_socket = NULL;
+}
+
+static void
+forward_key_events (GSWindow *window)
+{
+ window->priv->key_events = g_list_reverse (window->priv->key_events);
+
+ while (window->priv->key_events != NULL)
+ {
+ GdkEventKey *event = window->priv->key_events->data;
+
+ gtk_window_propagate_key_event (GTK_WINDOW (window), event);
+
+ gdk_event_free ((GdkEvent *)event);
+ window->priv->key_events = g_list_delete_link (window->priv->key_events,
+ window->priv->key_events);
+ }
+}
+
+static void
+remove_key_events (GSWindow *window)
+{
+ window->priv->key_events = g_list_reverse (window->priv->key_events);
+
+ while (window->priv->key_events)
+ {
+ GdkEventKey *event = window->priv->key_events->data;
+
+ gdk_event_free ((GdkEvent *)event);
+ window->priv->key_events = g_list_delete_link (window->priv->key_events,
+ window->priv->key_events);
+ }
+}
+
+static void
+lock_socket_show (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_child_focus (window->priv->lock_socket, GTK_DIR_TAB_FORWARD);
+
+ /* send queued events to the dialog */
+ forward_key_events (window);
+}
+
+static void
+lock_socket_destroyed (GtkWidget *widget,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (widget, lock_socket_show, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_socket_destroyed, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_plug_added, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_plug_removed, window);
+
+ window->priv->lock_socket = NULL;
+}
+
+static void
+create_keyboard_socket (GSWindow *window,
+ guint32 id)
+{
+ int height;
+
+ height = (gdk_screen_get_height (gtk_widget_get_screen (GTK_WIDGET (window)))) / 4;
+
+ window->priv->keyboard_socket = gtk_socket_new ();
+ gtk_widget_set_size_request (window->priv->keyboard_socket, -1, height);
+
+ g_signal_connect (window->priv->keyboard_socket, "destroy",
+ G_CALLBACK (keyboard_socket_destroyed), window);
+ g_signal_connect (window->priv->keyboard_socket, "plug_added",
+ G_CALLBACK (keyboard_plug_added), window);
+ g_signal_connect (window->priv->keyboard_socket, "plug_removed",
+ G_CALLBACK (keyboard_plug_removed), window);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->keyboard_socket, FALSE, FALSE, 0);
+ gtk_socket_add_id (GTK_SOCKET (window->priv->keyboard_socket), id);
+}
+
+/* adapted from gspawn.c */
+static int
+wait_on_child (int pid)
+{
+ int status;
+
+wait_again:
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ {
+ goto wait_again;
+ }
+ else if (errno == ECHILD)
+ {
+ ; /* do nothing, child already reaped */
+ }
+ else
+ {
+ gs_debug ("waitpid () should not fail in 'GSWindow'");
+ }
+ }
+
+ return status;
+}
+
+static void
+kill_keyboard_command (GSWindow *window)
+{
+ if (window->priv->keyboard_pid > 0)
+ {
+ signal_pid (window->priv->keyboard_pid, SIGTERM);
+ }
+}
+
+static void
+kill_dialog_command (GSWindow *window)
+{
+ /* If a dialog is up we need to signal it
+ and wait on it */
+ if (window->priv->lock_pid > 0)
+ {
+ signal_pid (window->priv->lock_pid, SIGTERM);
+ }
+}
+
+static void
+keyboard_command_finish (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ /* send a signal just in case */
+ kill_keyboard_command (window);
+
+ gs_debug ("Keyboard finished");
+
+ if (window->priv->keyboard_pid > 0)
+ {
+ int exit_status;
+
+ exit_status = wait_on_child (window->priv->keyboard_pid);
+
+ g_spawn_close_pid (window->priv->keyboard_pid);
+ window->priv->keyboard_pid = 0;
+ }
+}
+
+static gboolean
+keyboard_command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSWindow *window)
+{
+ gboolean finished = FALSE;
+
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ {
+ guint32 id;
+ char c;
+ gs_debug ("keyboard command output: %s", line);
+ if (1 == sscanf (line, " %" G_GUINT32_FORMAT " %c", &id, &c))
+ {
+ create_keyboard_socket (window, id);
+ }
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ window->priv->keyboard_watch_id = 0;
+ keyboard_command_finish (window);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+embed_keyboard (GSWindow *window)
+{
+ gboolean res;
+
+ if (! window->priv->keyboard_enabled
+ || window->priv->keyboard_command == NULL)
+ return;
+
+ gs_debug ("Adding embedded keyboard widget");
+
+ /* FIXME: verify command is safe */
+
+ gs_debug ("Running command: %s", window->priv->keyboard_command);
+
+ res = spawn_on_window (window,
+ window->priv->keyboard_command,
+ &window->priv->keyboard_pid,
+ (GIOFunc)keyboard_command_watch,
+ window,
+ &window->priv->keyboard_watch_id);
+ if (! res)
+ {
+ gs_debug ("Could not start command: %s", window->priv->keyboard_command);
+ }
+}
+
+static void
+create_lock_socket (GSWindow *window,
+ guint32 id)
+{
+ window->priv->lock_socket = gtk_socket_new ();
+ window->priv->lock_box = gtk_alignment_new (0.5, 0.5, 0, 0);
+ gtk_widget_show (window->priv->lock_box);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->lock_box, TRUE, TRUE, 0);
+
+ gtk_container_add (GTK_CONTAINER (window->priv->lock_box), window->priv->lock_socket);
+
+ g_signal_connect (window->priv->lock_socket, "show",
+ G_CALLBACK (lock_socket_show), window);
+ g_signal_connect (window->priv->lock_socket, "destroy",
+ G_CALLBACK (lock_socket_destroyed), window);
+ g_signal_connect (window->priv->lock_socket, "plug_added",
+ G_CALLBACK (lock_plug_added), window);
+ g_signal_connect (window->priv->lock_socket, "plug_removed",
+ G_CALLBACK (lock_plug_removed), window);
+
+ gtk_socket_add_id (GTK_SOCKET (window->priv->lock_socket), id);
+
+ if (window->priv->keyboard_enabled)
+ {
+ embed_keyboard (window);
+ }
+}
+
+static void
+gs_window_dialog_finish (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Dialog finished");
+
+ /* make sure we finish the keyboard thing too */
+ keyboard_command_finish (window);
+
+ /* send a signal just in case */
+ kill_dialog_command (window);
+
+ if (window->priv->lock_pid > 0)
+ {
+ int exit_status;
+
+ exit_status = wait_on_child (window->priv->lock_pid);
+
+ g_spawn_close_pid (window->priv->lock_pid);
+ window->priv->lock_pid = 0;
+ }
+
+ /* remove events for the case were we failed to show socket */
+ remove_key_events (window);
+}
+
+static void
+maybe_kill_dialog (GSWindow *window)
+{
+ if (!window->priv->dialog_shake_in_progress
+ && window->priv->dialog_quit_requested
+ && window->priv->lock_pid > 0)
+ {
+ kill (window->priv->lock_pid, SIGTERM);
+ }
+}
+
+/* very rudimentary animation for indicating an auth failure */
+static void
+shake_dialog (GSWindow *window)
+{
+ int i;
+ guint left;
+ guint right;
+
+ window->priv->dialog_shake_in_progress = TRUE;
+
+ for (i = 0; i < 9; i++)
+ {
+ if (i % 2 == 0)
+ {
+ left = 30;
+ right = 0;
+ }
+ else
+ {
+ left = 0;
+ right = 30;
+ }
+
+ if (! window->priv->lock_box)
+ {
+ break;
+ }
+
+ gtk_alignment_set_padding (GTK_ALIGNMENT (window->priv->lock_box),
+ 0, 0,
+ left,
+ right);
+
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ g_usleep (10000);
+ }
+
+ window->priv->dialog_shake_in_progress = FALSE;
+ maybe_kill_dialog (window);
+}
+
+static void
+window_set_dialog_up (GSWindow *window,
+ gboolean dialog_up)
+{
+ if (window->priv->dialog_up == dialog_up)
+ {
+ return;
+ }
+
+ window->priv->dialog_up = dialog_up;
+ g_object_notify (G_OBJECT (window), "dialog-up");
+}
+
+static void
+popdown_dialog (GSWindow *window)
+{
+ gs_window_dialog_finish (window);
+
+ gtk_widget_show (window->priv->drawing_area);
+
+ gs_window_clear (window);
+ set_invisible_cursor (GTK_WIDGET (window)->window, TRUE);
+
+ window_set_dialog_up (window, FALSE);
+
+ /* reset the pointer positions */
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+
+ if (window->priv->lock_box != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->lock_box));
+ window->priv->lock_box = NULL;
+ }
+
+ remove_popup_dialog_idle (window);
+ remove_command_watches (window);
+}
+
+static gboolean
+lock_command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSWindow *window)
+{
+ gboolean finished = FALSE;
+
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ gs_debug ("command output: %s", line);
+
+ if (strstr (line, "WINDOW ID=") != NULL)
+ {
+ guint32 id;
+ char c;
+ if (1 == sscanf (line, " WINDOW ID= %" G_GUINT32_FORMAT " %c", &id, &c))
+ {
+ create_lock_socket (window, id);
+ }
+ }
+ else if (strstr (line, "NOTICE=") != NULL)
+ {
+ if (strstr (line, "NOTICE=AUTH FAILED") != NULL)
+ {
+ shake_dialog (window);
+ }
+ }
+ else if (strstr (line, "RESPONSE=") != NULL)
+ {
+ if (strstr (line, "RESPONSE=OK") != NULL)
+ {
+ gs_debug ("Got OK response");
+ window->priv->dialog_response = DIALOG_RESPONSE_OK;
+ }
+ else
+ {
+ gs_debug ("Got CANCEL response");
+ window->priv->dialog_response = DIALOG_RESPONSE_CANCEL;
+ }
+ finished = TRUE;
+ }
+ else if (strstr (line, "REQUEST QUIT") != NULL)
+ {
+ gs_debug ("Got request for quit");
+ window->priv->dialog_quit_requested = TRUE;
+ maybe_kill_dialog (window);
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ popdown_dialog (window);
+
+ if (window->priv->dialog_response == DIALOG_RESPONSE_OK)
+ {
+ add_emit_deactivated_idle (window);
+ }
+
+ window->priv->lock_watch_id = 0;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+is_logout_enabled (GSWindow *window)
+{
+ double elapsed;
+
+ if (! window->priv->logout_enabled)
+ {
+ return FALSE;
+ }
+
+ if (! window->priv->logout_command)
+ {
+ return FALSE;
+ }
+
+ elapsed = g_timer_elapsed (window->priv->timer, NULL);
+
+ if (window->priv->logout_timeout < (elapsed * 1000))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_user_switch_enabled (GSWindow *window)
+{
+ return window->priv->user_switch_enabled;
+}
+
+static void
+popup_dialog (GSWindow *window)
+{
+ gboolean result;
+ char *tmp;
+ GString *command;
+
+ gs_debug ("Popping up dialog");
+
+ tmp = g_build_filename (LIBEXECDIR, "mate-screensaver-dialog", NULL);
+ command = g_string_new (tmp);
+ g_free (tmp);
+
+ if (is_logout_enabled (window))
+ {
+ command = g_string_append (command, " --enable-logout");
+ g_string_append_printf (command, " --logout-command='%s'", window->priv->logout_command);
+ }
+
+ if (window->priv->status_message)
+ {
+ char *quoted;
+
+ quoted = g_shell_quote (window->priv->status_message);
+ g_string_append_printf (command, " --status-message=%s", quoted);
+ g_free (quoted);
+ }
+
+ if (is_user_switch_enabled (window))
+ {
+ command = g_string_append (command, " --enable-switch");
+ }
+
+ if (gs_debug_enabled ())
+ {
+ command = g_string_append (command, " --verbose");
+ }
+
+ gtk_widget_hide (window->priv->drawing_area);
+
+ gs_window_clear_to_background_pixmap (window);
+
+ set_invisible_cursor (GTK_WIDGET (window)->window, FALSE);
+
+ window->priv->dialog_quit_requested = FALSE;
+ window->priv->dialog_shake_in_progress = FALSE;
+
+ result = spawn_on_window (window,
+ command->str,
+ &window->priv->lock_pid,
+ (GIOFunc)lock_command_watch,
+ window,
+ &window->priv->lock_watch_id);
+ if (! result)
+ {
+ gs_debug ("Could not start command: %s", command->str);
+ }
+
+ g_string_free (command, TRUE);
+}
+
+static gboolean
+popup_dialog_idle (GSWindow *window)
+{
+ popup_dialog (window);
+
+ window->priv->popup_dialog_idle_id = 0;
+
+ return FALSE;
+}
+
+void
+gs_window_request_unlock (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Requesting unlock");
+
+ if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (window)))
+ {
+ gs_debug ("Request unlock but window is not visible!");
+ return;
+ }
+
+ if (window->priv->lock_watch_id > 0)
+ {
+ return;
+ }
+
+ if (! window->priv->lock_enabled)
+ {
+ add_emit_deactivated_idle (window);
+
+ return;
+ }
+
+ if (window->priv->popup_dialog_idle_id == 0)
+ {
+ add_popup_dialog_idle (window);
+ }
+
+ window_set_dialog_up (window, TRUE);
+}
+
+void
+gs_window_cancel_unlock_request (GSWindow *window)
+{
+ /* FIXME: This is a bit of a hammer approach...
+ * Maybe we should send a delete-event to
+ * the plug?
+ */
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ popdown_dialog (window);
+}
+
+void
+gs_window_set_lock_enabled (GSWindow *window,
+ gboolean lock_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->lock_enabled == lock_enabled)
+ {
+ return;
+ }
+
+ window->priv->lock_enabled = lock_enabled;
+ g_object_notify (G_OBJECT (window), "lock-enabled");
+}
+
+void
+gs_window_set_screen (GSWindow *window,
+ GdkScreen *screen)
+{
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ gtk_window_set_screen (GTK_WINDOW (window), screen);
+}
+
+GdkScreen *
+gs_window_get_screen (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return GTK_WINDOW (window)->screen;
+}
+
+void
+gs_window_set_keyboard_enabled (GSWindow *window,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->keyboard_enabled = enabled;
+}
+
+void
+gs_window_set_keyboard_command (GSWindow *window,
+ const char *command)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->keyboard_command);
+
+ if (command != NULL)
+ {
+ window->priv->keyboard_command = g_strdup (command);
+ }
+ else
+ {
+ window->priv->keyboard_command = NULL;
+ }
+}
+
+void
+gs_window_set_logout_enabled (GSWindow *window,
+ gboolean logout_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->logout_enabled = logout_enabled;
+}
+
+void
+gs_window_set_user_switch_enabled (GSWindow *window,
+ gboolean user_switch_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->user_switch_enabled = user_switch_enabled;
+}
+
+void
+gs_window_set_logout_timeout (GSWindow *window,
+ glong logout_timeout)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (logout_timeout < 0)
+ {
+ window->priv->logout_timeout = 0;
+ }
+ else
+ {
+ window->priv->logout_timeout = logout_timeout;
+ }
+}
+
+void
+gs_window_set_logout_command (GSWindow *window,
+ const char *command)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->logout_command);
+
+ if (command)
+ {
+ window->priv->logout_command = g_strdup (command);
+ }
+ else
+ {
+ window->priv->logout_command = NULL;
+ }
+}
+
+void
+gs_window_set_status_message (GSWindow *window,
+ const char *status_message)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->status_message);
+ window->priv->status_message = g_strdup (status_message);
+}
+
+void
+gs_window_set_monitor (GSWindow *window,
+ int monitor)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->monitor == monitor)
+ {
+ return;
+ }
+
+ window->priv->monitor = monitor;
+
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+
+ g_object_notify (G_OBJECT (window), "monitor");
+}
+
+int
+gs_window_get_monitor (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), -1);
+
+ return window->priv->monitor;
+}
+
+static void
+gs_window_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSWindow *self;
+
+ self = GS_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOCK_ENABLED:
+ gs_window_set_lock_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ gs_window_set_keyboard_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ gs_window_set_keyboard_command (self, g_value_get_string (value));
+ break;
+ case PROP_LOGOUT_ENABLED:
+ gs_window_set_logout_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOGOUT_COMMAND:
+ gs_window_set_logout_command (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS_MESSAGE:
+ gs_window_set_status_message (self, g_value_get_string (value));
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ gs_window_set_logout_timeout (self, g_value_get_long (value));
+ break;
+ case PROP_MONITOR:
+ gs_window_set_monitor (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_window_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSWindow *self;
+
+ self = GS_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOCK_ENABLED:
+ g_value_set_boolean (value, self->priv->lock_enabled);
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ g_value_set_boolean (value, self->priv->keyboard_enabled);
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ g_value_set_string (value, self->priv->keyboard_command);
+ break;
+ case PROP_LOGOUT_ENABLED:
+ g_value_set_boolean (value, self->priv->logout_enabled);
+ break;
+ case PROP_LOGOUT_COMMAND:
+ g_value_set_string (value, self->priv->logout_command);
+ break;
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ g_value_set_long (value, self->priv->logout_timeout);
+ break;
+ case PROP_MONITOR:
+ g_value_set_int (value, self->priv->monitor);
+ break;
+ case PROP_OBSCURED:
+ g_value_set_boolean (value, self->priv->obscured);
+ break;
+ case PROP_DIALOG_UP:
+ g_value_set_boolean (value, self->priv->dialog_up);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+queue_key_event (GSWindow *window,
+ GdkEventKey *event)
+{
+ /* Eat the first return, enter, escape, or space */
+ if (window->priv->key_events == NULL
+ && (event->keyval == GDK_Return
+ || event->keyval == GDK_KP_Enter
+ || event->keyval == GDK_Escape
+ || event->keyval == GDK_space))
+ {
+ return;
+ }
+
+ /* Only cache MAX_QUEUED_EVENTS key events. If there are any more than this then
+ something is wrong */
+ /* Don't queue keys that may cause focus navigation in the dialog */
+ if (g_list_length (window->priv->key_events) < MAX_QUEUED_EVENTS
+ && event->keyval != GDK_Tab
+ && event->keyval != GDK_Up
+ && event->keyval != GDK_Down)
+ {
+ window->priv->key_events = g_list_prepend (window->priv->key_events,
+ gdk_event_copy ((GdkEvent *)event));
+ }
+}
+
+static gboolean
+maybe_handle_activity (GSWindow *window)
+{
+ gboolean handled;
+
+ handled = FALSE;
+
+ /* if we already have a socket then don't bother */
+ if (! window->priv->lock_socket
+ && GTK_WIDGET_IS_SENSITIVE (GTK_WIDGET (window)))
+ {
+ g_signal_emit (window, signals [ACTIVITY], 0, &handled);
+ }
+
+ return handled;
+}
+
+static gboolean
+gs_window_real_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ /*g_message ("KEY PRESS state: %u keyval %u", event->state, event->keyval);*/
+
+ /* Ignore brightness keys */
+ if (event->hardware_keycode == 101 || event->hardware_keycode == 212)
+ {
+ gs_debug ("Ignoring brightness keys");
+ return TRUE;
+ }
+
+ maybe_handle_activity (GS_WINDOW (widget));
+
+ queue_key_event (GS_WINDOW (widget), event);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->key_press_event)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->key_press_event (widget, event);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gs_window_real_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GSWindow *window;
+ gdouble distance;
+ gdouble min_distance;
+ gdouble min_percentage = 0.1;
+ GdkScreen *screen;
+
+ window = GS_WINDOW (widget);
+
+ screen = gs_window_get_screen (window);
+ min_distance = gdk_screen_get_width (screen) * min_percentage;
+
+ /* if the last position was not set then don't detect motion */
+ if (window->priv->last_x < 0 || window->priv->last_y < 0)
+ {
+ window->priv->last_x = event->x;
+ window->priv->last_y = event->y;
+
+ return FALSE;
+ }
+
+ /* just an approximate distance */
+ distance = MAX (ABS (window->priv->last_x - event->x),
+ ABS (window->priv->last_y - event->y));
+
+ if (distance > min_distance)
+ {
+ maybe_handle_activity (window);
+
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gs_window_real_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+ maybe_handle_activity (window);
+
+ return FALSE;
+}
+
+static gboolean
+gs_window_real_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+ maybe_handle_activity (window);
+
+ return FALSE;
+}
+
+static void
+gs_window_real_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GSWindow *window;
+ GtkBin *bin;
+ GdkRectangle old_geometry;
+ int position_changed = FALSE;
+ int size_changed = FALSE;
+
+ window = GS_WINDOW (widget);
+ bin = GTK_BIN (widget);
+
+ if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
+ {
+ gtk_widget_size_request (bin->child, requisition);
+ }
+
+ old_geometry = window->priv->geometry;
+
+ update_geometry (window);
+
+ requisition->width = window->priv->geometry.width;
+ requisition->height = window->priv->geometry.height;
+
+ if (! GTK_WIDGET_REALIZED (widget))
+ {
+ return;
+ }
+
+ if (old_geometry.width != window->priv->geometry.width ||
+ old_geometry.height != window->priv->geometry.height)
+ {
+ size_changed = TRUE;
+ }
+
+ if (old_geometry.x != window->priv->geometry.x ||
+ old_geometry.y != window->priv->geometry.y)
+ {
+ position_changed = TRUE;
+ }
+
+ gs_window_move_resize_window (window, position_changed, size_changed);
+}
+
+static gboolean
+gs_window_real_grab_broken (GtkWidget *widget,
+ GdkEventGrabBroken *event)
+{
+ if (event->grab_window != NULL)
+ {
+ gs_debug ("Grab broken on window %X %s, new grab on window %X",
+ (guint32) GDK_WINDOW_XID (event->window),
+ event->keyboard ? "keyboard" : "pointer",
+ (guint32) GDK_WINDOW_XID (event->grab_window));
+ }
+ else
+ {
+ gs_debug ("Grab broken on window %X %s, new grab is outside application",
+ (guint32) GDK_WINDOW_XID (event->window),
+ event->keyboard ? "keyboard" : "pointer");
+ }
+
+ return FALSE;
+}
+
+gboolean
+gs_window_is_obscured (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ return window->priv->obscured;
+}
+
+gboolean
+gs_window_is_dialog_up (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ return window->priv->dialog_up;
+}
+
+static void
+window_set_obscured (GSWindow *window,
+ gboolean obscured)
+{
+ if (window->priv->obscured == obscured)
+ {
+ return;
+ }
+
+ window->priv->obscured = obscured;
+ g_object_notify (G_OBJECT (window), "obscured");
+}
+
+static gboolean
+gs_window_real_visibility_notify_event (GtkWidget *widget,
+ GdkEventVisibility *event)
+{
+ switch (event->state)
+ {
+ case GDK_VISIBILITY_FULLY_OBSCURED:
+ window_set_obscured (GS_WINDOW (widget), TRUE);
+ break;
+ case GDK_VISIBILITY_PARTIAL:
+ break;
+ case GDK_VISIBILITY_UNOBSCURED:
+ window_set_obscured (GS_WINDOW (widget), FALSE);
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+gs_window_class_init (GSWindowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gs_window_finalize;
+ object_class->get_property = gs_window_get_property;
+ object_class->set_property = gs_window_set_property;
+
+ widget_class->show = gs_window_real_show;
+ widget_class->hide = gs_window_real_hide;
+ widget_class->realize = gs_window_real_realize;
+ widget_class->unrealize = gs_window_real_unrealize;
+ widget_class->key_press_event = gs_window_real_key_press_event;
+ widget_class->motion_notify_event = gs_window_real_motion_notify_event;
+ widget_class->button_press_event = gs_window_real_button_press_event;
+ widget_class->scroll_event = gs_window_real_scroll_event;
+ widget_class->size_request = gs_window_real_size_request;
+ widget_class->grab_broken_event = gs_window_real_grab_broken;
+ widget_class->visibility_notify_event = gs_window_real_visibility_notify_event;
+
+ g_type_class_add_private (klass, sizeof (GSWindowPrivate));
+
+ signals [ACTIVITY] =
+ g_signal_new ("activity",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWindowClass, activity),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+ signals [DEACTIVATED] =
+ g_signal_new ("deactivated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWindowClass, deactivated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_OBSCURED,
+ g_param_spec_boolean ("obscured",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_DIALOG_UP,
+ g_param_spec_boolean ("dialog-up",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_LOCK_ENABLED,
+ g_param_spec_boolean ("lock-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_ENABLED,
+ g_param_spec_boolean ("logout-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_TIMEOUT,
+ g_param_spec_long ("logout-timeout",
+ NULL,
+ NULL,
+ -1,
+ G_MAXLONG,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_COMMAND,
+ g_param_spec_string ("logout-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_STATUS_MESSAGE,
+ g_param_spec_string ("status-message",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_KEYBOARD_ENABLED,
+ g_param_spec_boolean ("keyboard-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_KEYBOARD_COMMAND,
+ g_param_spec_string ("keyboard-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_MONITOR,
+ g_param_spec_int ("monitor",
+ "Xinerama monitor",
+ "The monitor (in terms of Xinerama) which the window is on",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+}
+
+static void
+create_info_bar (GSWindow *window)
+{
+ window->priv->info_bar = gtk_info_bar_new ();
+ gtk_widget_set_no_show_all (window->priv->info_bar, TRUE);
+ gtk_box_pack_end (GTK_BOX (window->priv->vbox), window->priv->info_bar, FALSE, FALSE, 0);
+}
+
+static void
+gs_window_init (GSWindow *window)
+{
+ window->priv = GS_WINDOW_GET_PRIVATE (window);
+
+ window->priv->geometry.x = -1;
+ window->priv->geometry.y = -1;
+ window->priv->geometry.width = -1;
+ window->priv->geometry.height = -1;
+
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+
+ gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
+
+ gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
+ gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
+
+ gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
+
+ gtk_window_fullscreen (GTK_WINDOW (window));
+
+ gtk_widget_set_events (GTK_WIDGET (window),
+ gtk_widget_get_events (GTK_WIDGET (window))
+ | GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_EXPOSURE_MASK
+ | GDK_VISIBILITY_NOTIFY_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK);
+
+ window->priv->vbox = gtk_vbox_new (FALSE, 12);
+ gtk_widget_show (window->priv->vbox);
+ gtk_container_add (GTK_CONTAINER (window), window->priv->vbox);
+
+ window->priv->drawing_area = gtk_drawing_area_new ();
+ gtk_widget_show (window->priv->drawing_area);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->drawing_area, TRUE, TRUE, 0);
+ create_info_bar (window);
+
+ force_no_pixmap_background (window->priv->drawing_area);
+}
+
+static void
+remove_command_watches (GSWindow *window)
+{
+ if (window->priv->lock_watch_id != 0)
+ {
+ g_source_remove (window->priv->lock_watch_id);
+ window->priv->lock_watch_id = 0;
+ }
+ if (window->priv->keyboard_watch_id != 0)
+ {
+ g_source_remove (window->priv->keyboard_watch_id);
+ window->priv->keyboard_watch_id = 0;
+ }
+}
+
+static void
+gs_window_finalize (GObject *object)
+{
+ GSWindow *window;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_WINDOW (object));
+
+ window = GS_WINDOW (object);
+
+ g_return_if_fail (window->priv != NULL);
+
+ g_free (window->priv->logout_command);
+ g_free (window->priv->keyboard_command);
+
+ if (window->priv->info_bar_timer_id > 0)
+ {
+ g_source_remove (window->priv->info_bar_timer_id);
+ }
+
+ remove_watchdog_timer (window);
+ remove_popup_dialog_idle (window);
+
+ if (window->priv->timer)
+ {
+ g_timer_destroy (window->priv->timer);
+ }
+
+ remove_key_events (window);
+
+ remove_command_watches (window);
+
+ gs_window_dialog_finish (window);
+
+ if (window->priv->background_pixmap)
+ {
+ g_object_unref (window->priv->background_pixmap);
+ }
+
+ G_OBJECT_CLASS (gs_window_parent_class)->finalize (object);
+}
+
+GSWindow *
+gs_window_new (GdkScreen *screen,
+ int monitor,
+ gboolean lock_enabled)
+{
+ GObject *result;
+
+ result = g_object_new (GS_TYPE_WINDOW,
+ "type", GTK_WINDOW_POPUP,
+ "screen", screen,
+ "monitor", monitor,
+ "lock-enabled", lock_enabled,
+ NULL);
+
+ return GS_WINDOW (result);
+}
diff --git a/src/gs-window-x11.c.orig b/src/gs-window-x11.c.orig
new file mode 100644
index 0000000..f7c3c70
--- /dev/null
+++ b/src/gs-window-x11.c.orig
@@ -0,0 +1,2647 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2008 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <string.h>
+
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+
+#include "gs-window.h"
+#include "gs-marshal.h"
+#include "subprocs.h"
+#include "gs-debug.h"
+
+#ifdef HAVE_SHAPE_EXT
+#include <X11/extensions/shape.h>
+#endif
+
+static void gs_window_class_init (GSWindowClass *klass);
+static void gs_window_init (GSWindow *window);
+static void gs_window_finalize (GObject *object);
+
+static gboolean popup_dialog_idle (GSWindow *window);
+static void gs_window_dialog_finish (GSWindow *window);
+static void remove_command_watches (GSWindow *window);
+
+enum
+{
+ DIALOG_RESPONSE_CANCEL,
+ DIALOG_RESPONSE_OK
+};
+
+#define MAX_QUEUED_EVENTS 16
+#define INFO_BAR_SECONDS 30
+
+#define GS_WINDOW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GS_TYPE_WINDOW, GSWindowPrivate))
+
+struct GSWindowPrivate
+{
+ int monitor;
+
+ GdkRectangle geometry;
+ guint obscured : 1;
+ guint dialog_up : 1;
+
+ guint lock_enabled : 1;
+ guint user_switch_enabled : 1;
+ guint logout_enabled : 1;
+ guint keyboard_enabled : 1;
+
+ guint64 logout_timeout;
+ char *logout_command;
+ char *keyboard_command;
+ char *status_message;
+
+ GtkWidget *vbox;
+ GtkWidget *drawing_area;
+ GtkWidget *lock_box;
+ GtkWidget *lock_socket;
+ GtkWidget *keyboard_socket;
+ GtkWidget *info_bar;
+ GtkWidget *info_content;
+
+ GdkPixmap *background_pixmap;
+
+ guint popup_dialog_idle_id;
+
+ guint dialog_map_signal_id;
+ guint dialog_unmap_signal_id;
+ guint dialog_response_signal_id;
+
+ guint watchdog_timer_id;
+ guint info_bar_timer_id;
+
+ gint lock_pid;
+ gint lock_watch_id;
+ gint dialog_response;
+ gboolean dialog_quit_requested;
+ gboolean dialog_shake_in_progress;
+
+ gint keyboard_pid;
+ gint keyboard_watch_id;
+
+ GList *key_events;
+
+ gdouble last_x;
+ gdouble last_y;
+
+ GTimer *timer;
+
+#ifdef HAVE_SHAPE_EXT
+ int shape_event_base;
+#endif
+};
+
+enum
+{
+ ACTIVITY,
+ DEACTIVATED,
+ LAST_SIGNAL
+};
+
+enum
+{
+ PROP_0,
+ PROP_OBSCURED,
+ PROP_DIALOG_UP,
+ PROP_LOCK_ENABLED,
+ PROP_LOGOUT_ENABLED,
+ PROP_KEYBOARD_ENABLED,
+ PROP_KEYBOARD_COMMAND,
+ PROP_LOGOUT_COMMAND,
+ PROP_LOGOUT_TIMEOUT,
+ PROP_MONITOR,
+ PROP_STATUS_MESSAGE
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (GSWindow, gs_window, GTK_TYPE_WINDOW)
+
+static void
+set_invisible_cursor (GdkWindow *window,
+ gboolean invisible)
+{
+ GdkBitmap *empty_bitmap;
+ GdkCursor *cursor = NULL;
+ GdkColor useless;
+ char invisible_cursor_bits [] = { 0x0 };
+
+ if (invisible)
+ {
+ useless.red = useless.green = useless.blue = 0;
+ useless.pixel = 0;
+
+ empty_bitmap = gdk_bitmap_create_from_data (window,
+ invisible_cursor_bits,
+ 1, 1);
+
+ cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
+ empty_bitmap,
+ &useless,
+ &useless, 0, 0);
+
+ g_object_unref (empty_bitmap);
+ }
+
+ gdk_window_set_cursor (window, cursor);
+
+ if (cursor)
+ {
+ gdk_cursor_unref (cursor);
+ }
+}
+
+/* derived from tomboy */
+static void
+gs_window_override_user_time (GSWindow *window)
+{
+ guint32 ev_time = gtk_get_current_event_time ();
+
+ if (ev_time == 0)
+ {
+ gint ev_mask = gtk_widget_get_events (GTK_WIDGET (window));
+ if (!(ev_mask & GDK_PROPERTY_CHANGE_MASK))
+ {
+ gtk_widget_add_events (GTK_WIDGET (window),
+ GDK_PROPERTY_CHANGE_MASK);
+ }
+
+ /*
+ * NOTE: Last resort for D-BUS or other non-interactive
+ * openings. Causes roundtrip to server. Lame.
+ */
+ ev_time = gdk_x11_get_server_time (GTK_WIDGET (window)->window);
+ }
+
+ gdk_x11_window_set_user_time (GTK_WIDGET (window)->window, ev_time);
+}
+
+static void
+force_no_pixmap_background (GtkWidget *widget)
+{
+ static gboolean first_time = TRUE;
+
+ if (first_time)
+ {
+ gtk_rc_parse_string ("\n"
+ " style \"gs-theme-engine-style\"\n"
+ " {\n"
+ " bg_pixmap[NORMAL] = \"<none>\"\n"
+ " bg_pixmap[INSENSITIVE] = \"<none>\"\n"
+ " bg_pixmap[ACTIVE] = \"<none>\"\n"
+ " bg_pixmap[PRELIGHT] = \"<none>\"\n"
+ " bg[NORMAL] = { 0.0, 0.0, 0.0 }\n"
+ " bg[INSENSITIVE] = { 0.0, 0.0, 0.0 }\n"
+ " bg[ACTIVE] = { 0.0, 0.0, 0.0 }\n"
+ " bg[PRELIGHT] = { 0.0, 0.0, 0.0 }\n"
+ " }\n"
+ " widget \"gs-window-drawing-area*\" style : highest \"gs-theme-engine-style\"\n"
+ "\n");
+ first_time = FALSE;
+ }
+
+ gtk_widget_set_name (widget, "gs-window-drawing-area");
+}
+
+static void
+clear_children (Window window)
+{
+ Window root;
+ Window parent;
+ Window *children;
+ unsigned int n_children;
+ int status;
+
+ children = NULL;
+ status = XQueryTree (GDK_DISPLAY (), window, &root, &parent, &children, &n_children);
+
+ if (status == 0)
+ {
+ if (children)
+ {
+ XFree (children);
+ }
+ return;
+ }
+
+ if (children)
+ {
+ while (n_children)
+ {
+ Window child;
+
+ child = children [--n_children];
+
+ XClearWindow (GDK_DISPLAY (), child);
+ clear_children (child);
+ }
+
+ XFree (children);
+ }
+}
+
+static void
+widget_clear_all_children (GtkWidget *widget)
+{
+ GdkWindow *w;
+
+ gs_debug ("Clearing all child windows");
+
+ gdk_error_trap_push ();
+
+ w = widget->window;
+
+ clear_children (GDK_WINDOW_XID (w));
+
+ gdk_display_sync (gtk_widget_get_display (widget));
+ gdk_error_trap_pop ();
+}
+
+void
+gs_window_set_background_pixmap (GSWindow *window,
+ GdkPixmap *pixmap)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->background_pixmap != NULL)
+ {
+ g_object_unref (window->priv->background_pixmap);
+ }
+
+ if (pixmap != NULL)
+ {
+ window->priv->background_pixmap = g_object_ref (pixmap);
+ gdk_window_set_back_pixmap (GTK_WIDGET (window)->window,
+ pixmap,
+ FALSE);
+ }
+}
+
+static void
+gs_window_clear_to_background_pixmap (GSWindow *window)
+{
+ GtkStateType state;
+ GtkStyle *style;
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (window)))
+ {
+ return;
+ }
+
+ if (window->priv->background_pixmap == NULL)
+ {
+ /* don't allow null pixmaps */
+ return;
+ }
+
+ gs_debug ("Clearing window to background pixmap");
+
+ style = gtk_style_copy (GTK_WIDGET (window)->style);
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (GTK_WIDGET (window)->style->bg_pixmap))
+ {
+
+ if (style->bg_pixmap[state] != NULL)
+ {
+ g_object_unref (style->bg_pixmap[state]);
+ }
+
+ style->bg_pixmap[state] = g_object_ref (window->priv->background_pixmap);
+ state++;
+ }
+
+ gtk_widget_set_style (GTK_WIDGET (window), style);
+ g_object_unref (style);
+
+ if (window->priv->background_pixmap != NULL)
+ {
+ gdk_window_set_back_pixmap (GTK_WIDGET (window)->window,
+ window->priv->background_pixmap,
+ FALSE);
+ }
+
+ gdk_window_clear (GTK_WIDGET (window)->window);
+
+ gdk_flush ();
+}
+
+static void
+clear_widget (GtkWidget *widget)
+{
+ GdkColor color = { 0, 0x0000, 0x0000, 0x0000 };
+ GdkColormap *colormap;
+ GtkStateType state;
+ GtkStyle *style;
+
+ if (! GTK_WIDGET_VISIBLE (widget))
+ {
+ return;
+ }
+
+ gs_debug ("Clearing widget");
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg))
+ {
+ gtk_widget_modify_bg (widget, state, &color);
+ state++;
+ }
+
+ style = gtk_style_copy (widget->style);
+
+ state = (GtkStateType) 0;
+ while (state < (GtkStateType) G_N_ELEMENTS (widget->style->bg_pixmap))
+ {
+
+ if (style->bg_pixmap[state] != NULL)
+ {
+ g_object_unref (style->bg_pixmap[state]);
+ }
+
+ style->bg_pixmap[state] = NULL;
+ state++;
+ }
+
+ colormap = gdk_drawable_get_colormap (widget->window);
+ gdk_colormap_alloc_color (colormap, &color, FALSE, TRUE);
+ gdk_window_set_background (widget->window, &color);
+
+ gtk_widget_set_style (widget, style);
+ g_object_unref (style);
+
+ gdk_window_clear (widget->window);
+
+ /* If a screensaver theme adds child windows we need to clear them too */
+ widget_clear_all_children (widget);
+
+ gdk_flush ();
+}
+
+void
+gs_window_clear (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ clear_widget (GTK_WIDGET (window));
+ clear_widget (window->priv->drawing_area);
+}
+
+static GdkRegion *
+get_outside_region (GSWindow *window)
+{
+ int i;
+ GdkRegion *region;
+
+ region = gdk_region_new ();
+ for (i = 0; i < window->priv->monitor; i++)
+ {
+ GdkRectangle geometry;
+
+ gdk_screen_get_monitor_geometry (GTK_WINDOW (window)->screen,
+ i, &geometry);
+ gdk_region_union_with_rect (region, &geometry);
+ }
+
+ return region;
+}
+
+static void
+update_geometry (GSWindow *window)
+{
+ GdkRectangle geometry;
+ GdkRegion *outside_region;
+ GdkRegion *monitor_region;
+
+ outside_region = get_outside_region (window);
+
+ gdk_screen_get_monitor_geometry (GTK_WINDOW (window)->screen,
+ window->priv->monitor,
+ &geometry);
+ gs_debug ("got geometry for monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ geometry.x,
+ geometry.y,
+ geometry.width,
+ geometry.height);
+ monitor_region = gdk_region_rectangle (&geometry);
+ gdk_region_subtract (monitor_region, outside_region);
+ gdk_region_destroy (outside_region);
+
+ gdk_region_get_clipbox (monitor_region, &geometry);
+ gdk_region_destroy (monitor_region);
+
+ gs_debug ("using geometry for monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ geometry.x,
+ geometry.y,
+ geometry.width,
+ geometry.height);
+
+ window->priv->geometry.x = geometry.x;
+ window->priv->geometry.y = geometry.y;
+ window->priv->geometry.width = geometry.width;
+ window->priv->geometry.height = geometry.height;
+}
+
+static void
+screen_size_changed (GdkScreen *screen,
+ GSWindow *window)
+{
+ gs_debug ("Got screen size changed signal");
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+}
+
+/* copied from panel-toplevel.c */
+static void
+gs_window_move_resize_window (GSWindow *window,
+ gboolean move,
+ gboolean resize)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (window);
+
+ g_assert (GTK_WIDGET_REALIZED (widget));
+
+ gs_debug ("Move and/or resize window on monitor %d: x=%d y=%d w=%d h=%d",
+ window->priv->monitor,
+ window->priv->geometry.x,
+ window->priv->geometry.y,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+
+ if (move && resize)
+ {
+ gdk_window_move_resize (widget->window,
+ window->priv->geometry.x,
+ window->priv->geometry.y,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+ }
+ else if (move)
+ {
+ gdk_window_move (widget->window,
+ window->priv->geometry.x,
+ window->priv->geometry.y);
+ }
+ else if (resize)
+ {
+ gdk_window_resize (widget->window,
+ window->priv->geometry.width,
+ window->priv->geometry.height);
+ }
+}
+
+static void
+gs_window_real_unrealize (GtkWidget *widget)
+{
+ g_signal_handlers_disconnect_by_func (gtk_window_get_screen (GTK_WINDOW (widget)),
+ screen_size_changed,
+ widget);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->unrealize)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->unrealize (widget);
+ }
+}
+
+/* copied from gdk */
+extern char **environ;
+
+static gchar **
+spawn_make_environment_for_screen (GdkScreen *screen,
+ gchar **envp)
+{
+ gchar **retval = NULL;
+ gchar *display_name;
+ gint display_index = -1;
+ gint i, env_len;
+
+ g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
+
+ if (envp == NULL)
+ envp = environ;
+
+ for (env_len = 0; envp[env_len]; env_len++)
+ if (strncmp (envp[env_len], "DISPLAY", strlen ("DISPLAY")) == 0)
+ display_index = env_len;
+
+ retval = g_new (char *, env_len + 1);
+ retval[env_len] = NULL;
+
+ display_name = gdk_screen_make_display_name (screen);
+
+ for (i = 0; i < env_len; i++)
+ if (i == display_index)
+ retval[i] = g_strconcat ("DISPLAY=", display_name, NULL);
+ else
+ retval[i] = g_strdup (envp[i]);
+
+ g_assert (i == env_len);
+
+ g_free (display_name);
+
+ return retval;
+}
+
+static gboolean
+spawn_command_line_on_screen_sync (GdkScreen *screen,
+ const gchar *command_line,
+ char **standard_output,
+ char **standard_error,
+ int *exit_status,
+ GError **error)
+{
+ char **argv = NULL;
+ char **envp = NULL;
+ gboolean retval;
+
+ g_return_val_if_fail (command_line != NULL, FALSE);
+
+ if (! g_shell_parse_argv (command_line, NULL, &argv, error))
+ {
+ return FALSE;
+ }
+
+ envp = spawn_make_environment_for_screen (screen, NULL);
+
+ retval = g_spawn_sync (NULL,
+ argv,
+ envp,
+ G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ standard_output,
+ standard_error,
+ exit_status,
+ error);
+
+ g_strfreev (argv);
+ g_strfreev (envp);
+
+ return retval;
+}
+
+static GdkVisual *
+get_best_visual_for_screen (GdkScreen *screen)
+{
+ char *command;
+ char *std_output;
+ int exit_status;
+ GError *error;
+ unsigned long v;
+ char c;
+ GdkVisual *visual;
+ gboolean res;
+
+ visual = NULL;
+
+ command = g_build_filename (LIBEXECDIR, "mate-screensaver-gl-helper", NULL);
+
+ error = NULL;
+ std_output = NULL;
+ res = spawn_command_line_on_screen_sync (screen,
+ command,
+ &std_output,
+ NULL,
+ &exit_status,
+ &error);
+ if (! res)
+ {
+ gs_debug ("Could not run command '%s': %s", command, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (1 == sscanf (std_output, "0x%lx %c", &v, &c))
+ {
+ if (v != 0)
+ {
+ VisualID visual_id;
+
+ visual_id = (VisualID) v;
+ visual = gdkx_visual_get (visual_id);
+
+ gs_debug ("Found best GL visual for screen %d: 0x%x",
+ gdk_screen_get_number (screen),
+ (unsigned int) visual_id);
+ }
+ }
+out:
+ g_free (std_output);
+ g_free (command);
+
+ return visual;
+}
+
+static GdkColormap *
+get_best_colormap_for_screen (GdkScreen *screen)
+{
+ GdkColormap *colormap;
+ GdkVisual *visual;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ visual = get_best_visual_for_screen (screen);
+
+ colormap = NULL;
+ if (visual != NULL)
+ {
+ colormap = gdk_colormap_new (visual, FALSE);
+ }
+
+ return colormap;
+}
+
+static void
+widget_set_best_colormap (GtkWidget *widget)
+{
+ GdkColormap *colormap;
+
+ g_return_if_fail (widget != NULL);
+
+ colormap = get_best_colormap_for_screen (gtk_widget_get_screen (widget));
+ if (colormap != NULL)
+ {
+ gtk_widget_set_colormap (widget, colormap);
+ g_object_unref (colormap);
+ }
+}
+
+static void
+gs_window_real_realize (GtkWidget *widget)
+{
+ widget_set_best_colormap (widget);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->realize)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->realize (widget);
+ }
+
+ gs_window_override_user_time (GS_WINDOW (widget));
+
+ gs_window_move_resize_window (GS_WINDOW (widget), TRUE, TRUE);
+
+ g_signal_connect (gtk_window_get_screen (GTK_WINDOW (widget)),
+ "size_changed",
+ G_CALLBACK (screen_size_changed),
+ widget);
+}
+
+/* every so often we should raise the window in case
+ another window has somehow gotten on top */
+static gboolean
+watchdog_timer (GSWindow *window)
+{
+ GtkWidget *widget = GTK_WIDGET (window);
+
+ gdk_window_focus (widget->window, GDK_CURRENT_TIME);
+
+ return TRUE;
+}
+
+static void
+remove_watchdog_timer (GSWindow *window)
+{
+ if (window->priv->watchdog_timer_id != 0)
+ {
+ g_source_remove (window->priv->watchdog_timer_id);
+ window->priv->watchdog_timer_id = 0;
+ }
+}
+
+static void
+add_watchdog_timer (GSWindow *window,
+ glong timeout)
+{
+ window->priv->watchdog_timer_id = g_timeout_add (timeout,
+ (GSourceFunc)watchdog_timer,
+ window);
+}
+
+static void
+remove_popup_dialog_idle (GSWindow *window)
+{
+ if (window->priv->popup_dialog_idle_id != 0)
+ {
+ g_source_remove (window->priv->popup_dialog_idle_id);
+ window->priv->popup_dialog_idle_id = 0;
+ }
+}
+
+static void
+add_popup_dialog_idle (GSWindow *window)
+{
+ window->priv->popup_dialog_idle_id = g_idle_add ((GSourceFunc)popup_dialog_idle, window);
+}
+
+static gboolean
+emit_deactivated_idle (GSWindow *window)
+{
+ g_signal_emit (window, signals [DEACTIVATED], 0);
+
+ return FALSE;
+}
+
+static void
+add_emit_deactivated_idle (GSWindow *window)
+{
+ g_idle_add ((GSourceFunc)emit_deactivated_idle, window);
+}
+
+static void
+gs_window_raise (GSWindow *window)
+{
+ GdkWindow *win;
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Raising screensaver window");
+
+ win = GTK_WIDGET (window)->window;
+
+ gdk_window_raise (win);
+}
+
+static gboolean
+x11_window_is_ours (Window window)
+{
+ GdkWindow *gwindow;
+ gboolean ret;
+
+ ret = FALSE;
+
+ gwindow = gdk_window_lookup (window);
+ if (gwindow && (window != GDK_ROOT_WINDOW ()))
+ {
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+#ifdef HAVE_SHAPE_EXT
+static void
+unshape_window (GSWindow *window)
+{
+ gdk_window_shape_combine_region (GTK_WIDGET (window)->window,
+ NULL,
+ 0,
+ 0);
+}
+#endif
+
+static void
+gs_window_xevent (GSWindow *window,
+ GdkXEvent *xevent)
+{
+ XEvent *ev;
+
+ ev = xevent;
+
+ /* MapNotify is used to tell us when new windows are mapped.
+ ConfigureNofify is used to tell us when windows are raised. */
+ switch (ev->xany.type)
+ {
+ case MapNotify:
+ {
+ XMapEvent *xme = &ev->xmap;
+
+ if (! x11_window_is_ours (xme->window))
+ {
+ gs_window_raise (window);
+ }
+ else
+ {
+ gs_debug ("not raising our windows");
+ }
+
+ break;
+ }
+ case ConfigureNotify:
+ {
+ XConfigureEvent *xce = &ev->xconfigure;
+
+ if (! x11_window_is_ours (xce->window))
+ {
+ gs_window_raise (window);
+ }
+ else
+ {
+ gs_debug ("not raising our windows");
+ }
+
+ break;
+ }
+ default:
+ /* extension events */
+#ifdef HAVE_SHAPE_EXT
+ if (ev->xany.type == (window->priv->shape_event_base + ShapeNotify))
+ {
+ /*XShapeEvent *xse = (XShapeEvent *) ev;*/
+ unshape_window (window);
+ gs_debug ("Window was reshaped!");
+ }
+#endif
+
+ break;
+ }
+
+}
+
+static GdkFilterReturn
+xevent_filter (GdkXEvent *xevent,
+ GdkEvent *event,
+ GSWindow *window)
+{
+ gs_window_xevent (window, xevent);
+
+ return GDK_FILTER_CONTINUE;
+}
+
+static void
+select_popup_events (void)
+{
+ XWindowAttributes attr;
+ unsigned long events;
+
+ gdk_error_trap_push ();
+
+ memset (&attr, 0, sizeof (attr));
+ XGetWindowAttributes (GDK_DISPLAY (), GDK_ROOT_WINDOW (), &attr);
+
+ events = SubstructureNotifyMask | attr.your_event_mask;
+ XSelectInput (GDK_DISPLAY (), GDK_ROOT_WINDOW (), events);
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+}
+
+static void
+window_select_shape_events (GSWindow *window)
+{
+#ifdef HAVE_SHAPE_EXT
+ unsigned long events;
+ int shape_error_base;
+
+ gdk_error_trap_push ();
+
+ if (XShapeQueryExtension (GDK_DISPLAY (), &window->priv->shape_event_base, &shape_error_base))
+ {
+ events = ShapeNotifyMask;
+ XShapeSelectInput (GDK_DISPLAY (), GDK_WINDOW_XID (GTK_WIDGET (window)->window), events);
+ }
+
+ gdk_display_sync (gdk_display_get_default ());
+ gdk_error_trap_pop ();
+#endif
+}
+
+static void
+gs_window_real_show (GtkWidget *widget)
+{
+ GSWindow *window;
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->show)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->show (widget);
+ }
+
+ gs_window_clear (GS_WINDOW (widget));
+
+ set_invisible_cursor (widget->window, TRUE);
+
+ window = GS_WINDOW (widget);
+ if (window->priv->timer)
+ {
+ g_timer_destroy (window->priv->timer);
+ }
+ window->priv->timer = g_timer_new ();
+
+ remove_watchdog_timer (window);
+ add_watchdog_timer (window, 30000);
+
+ select_popup_events ();
+ window_select_shape_events (window);
+ gdk_window_add_filter (NULL, (GdkFilterFunc)xevent_filter, window);
+}
+
+static void
+set_info_text_and_icon (GSWindow *window,
+ const char *icon_stock_id,
+ const char *primary_text,
+ const char *secondary_text)
+{
+ GtkWidget *content_area;
+ GtkWidget *hbox_content;
+ GtkWidget *image;
+ GtkWidget *vbox;
+ gchar *primary_markup;
+ gchar *secondary_markup;
+ GtkWidget *primary_label;
+ GtkWidget *secondary_label;
+
+ hbox_content = gtk_hbox_new (FALSE, 8);
+ gtk_widget_show (hbox_content);
+
+ image = gtk_image_new_from_stock (icon_stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_widget_show (image);
+ gtk_box_pack_start (GTK_BOX (hbox_content), image, FALSE, FALSE, 0);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (vbox);
+ gtk_box_pack_start (GTK_BOX (hbox_content), vbox, FALSE, FALSE, 0);
+
+ primary_markup = g_strdup_printf ("<b>%s</b>", primary_text);
+ primary_label = gtk_label_new (primary_markup);
+ g_free (primary_markup);
+ gtk_widget_show (primary_label);
+ gtk_box_pack_start (GTK_BOX (vbox), primary_label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (primary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (primary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (primary_label), 0, 0.5);
+
+ if (secondary_text != NULL)
+ {
+ secondary_markup = g_strdup_printf ("<small>%s</small>",
+ secondary_text);
+ secondary_label = gtk_label_new (secondary_markup);
+ g_free (secondary_markup);
+ gtk_widget_show (secondary_label);
+ gtk_box_pack_start (GTK_BOX (vbox), secondary_label, TRUE, TRUE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (secondary_label), TRUE);
+ gtk_label_set_line_wrap (GTK_LABEL (secondary_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (secondary_label), 0, 0.5);
+ }
+
+ /* remove old content */
+ content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (window->priv->info_bar));
+ if (window->priv->info_content != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (content_area), window->priv->info_content);
+ }
+ gtk_box_pack_start (GTK_BOX (content_area),
+ hbox_content,
+ TRUE, FALSE, 0);
+ window->priv->info_content = hbox_content;
+}
+
+static gboolean
+info_bar_timeout (GSWindow *window)
+{
+ window->priv->info_bar_timer_id = 0;
+ gtk_widget_hide (window->priv->info_bar);
+ return FALSE;
+}
+
+void
+gs_window_show_message (GSWindow *window,
+ const char *summary,
+ const char *body,
+ const char *icon)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ set_info_text_and_icon (window,
+ icon,
+ summary,
+ body);
+ gtk_widget_show (window->priv->info_bar);
+
+ if (window->priv->info_bar_timer_id > 0)
+ {
+ g_source_remove (window->priv->info_bar_timer_id);
+ }
+
+ window->priv->info_bar_timer_id = g_timeout_add_seconds (INFO_BAR_SECONDS,
+ (GSourceFunc)info_bar_timeout,
+ window);
+}
+
+void
+gs_window_show (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gtk_widget_show (GTK_WIDGET (window));
+}
+
+static void
+gs_window_real_hide (GtkWidget *widget)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+
+ gdk_window_remove_filter (NULL, (GdkFilterFunc)xevent_filter, window);
+
+ remove_watchdog_timer (window);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->hide)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->hide (widget);
+ }
+}
+
+void
+gs_window_destroy (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_window_cancel_unlock_request (window);
+
+ gtk_widget_destroy (GTK_WIDGET (window));
+}
+
+GdkWindow *
+gs_window_get_gdk_window (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return GTK_WIDGET (window)->window;
+}
+
+GtkWidget *
+gs_window_get_drawing_area (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return window->priv->drawing_area;
+}
+
+/* just for debugging */
+static gboolean
+error_watch (GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ gboolean finished = FALSE;
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ gs_debug ("command error output: %s", line);
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+spawn_on_window (GSWindow *window,
+ char *command,
+ int *pid,
+ GIOFunc watch_func,
+ gpointer user_data,
+ gint *watch_id)
+{
+ int argc;
+ char **argv;
+ GError *error;
+ gboolean result;
+ GIOChannel *channel;
+ int standard_output;
+ int standard_error;
+ int child_pid;
+ int id;
+
+ error = NULL;
+ if (! g_shell_parse_argv (command, &argc, &argv, &error))
+ {
+ gs_debug ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ error = NULL;
+ result = gdk_spawn_on_screen_with_pipes (GTK_WINDOW (window)->screen,
+ NULL,
+ argv,
+ NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
+ NULL,
+ NULL,
+ &child_pid,
+ NULL,
+ &standard_output,
+ &standard_error,
+ &error);
+
+ if (! result)
+ {
+ gs_debug ("Could not start command '%s': %s", command, error->message);
+ g_error_free (error);
+ g_strfreev (argv);
+ return FALSE;
+ }
+
+ if (pid != NULL)
+ {
+ *pid = child_pid;
+ }
+ else
+ {
+ g_spawn_close_pid (child_pid);
+ }
+
+ /* output channel */
+ channel = g_io_channel_unix_new (standard_output);
+ 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);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ watch_func,
+ user_data);
+ if (watch_id != NULL)
+ {
+ *watch_id = id;
+ }
+ g_io_channel_unref (channel);
+
+ /* error channel */
+ channel = g_io_channel_unix_new (standard_error);
+ 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);
+ id = g_io_add_watch (channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ error_watch,
+ NULL);
+ g_io_channel_unref (channel);
+
+ g_strfreev (argv);
+
+ return result;
+}
+
+static void
+lock_plug_added (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_show (widget);
+}
+
+static gboolean
+lock_plug_removed (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_hide (widget);
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->lock_box));
+ window->priv->lock_box = NULL;
+
+ return TRUE;
+}
+
+static void
+keyboard_plug_added (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_show (widget);
+}
+
+static gboolean
+keyboard_plug_removed (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_hide (widget);
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->keyboard_socket));
+
+ return TRUE;
+}
+
+static void
+keyboard_socket_destroyed (GtkWidget *widget,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (widget, keyboard_socket_destroyed, window);
+ g_signal_handlers_disconnect_by_func (widget, keyboard_plug_added, window);
+ g_signal_handlers_disconnect_by_func (widget, keyboard_plug_removed, window);
+
+ window->priv->keyboard_socket = NULL;
+}
+
+static void
+forward_key_events (GSWindow *window)
+{
+ window->priv->key_events = g_list_reverse (window->priv->key_events);
+
+ while (window->priv->key_events != NULL)
+ {
+ GdkEventKey *event = window->priv->key_events->data;
+
+ gtk_window_propagate_key_event (GTK_WINDOW (window), event);
+
+ gdk_event_free ((GdkEvent *)event);
+ window->priv->key_events = g_list_delete_link (window->priv->key_events,
+ window->priv->key_events);
+ }
+}
+
+static void
+remove_key_events (GSWindow *window)
+{
+ window->priv->key_events = g_list_reverse (window->priv->key_events);
+
+ while (window->priv->key_events)
+ {
+ GdkEventKey *event = window->priv->key_events->data;
+
+ gdk_event_free ((GdkEvent *)event);
+ window->priv->key_events = g_list_delete_link (window->priv->key_events,
+ window->priv->key_events);
+ }
+}
+
+static void
+lock_socket_show (GtkWidget *widget,
+ GSWindow *window)
+{
+ gtk_widget_child_focus (window->priv->lock_socket, GTK_DIR_TAB_FORWARD);
+
+ /* send queued events to the dialog */
+ forward_key_events (window);
+}
+
+static void
+lock_socket_destroyed (GtkWidget *widget,
+ GSWindow *window)
+{
+ g_signal_handlers_disconnect_by_func (widget, lock_socket_show, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_socket_destroyed, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_plug_added, window);
+ g_signal_handlers_disconnect_by_func (widget, lock_plug_removed, window);
+
+ window->priv->lock_socket = NULL;
+}
+
+static void
+create_keyboard_socket (GSWindow *window,
+ guint32 id)
+{
+ int height;
+
+ height = (gdk_screen_get_height (gtk_widget_get_screen (GTK_WIDGET (window)))) / 4;
+
+ window->priv->keyboard_socket = gtk_socket_new ();
+ gtk_widget_set_size_request (window->priv->keyboard_socket, -1, height);
+
+ g_signal_connect (window->priv->keyboard_socket, "destroy",
+ G_CALLBACK (keyboard_socket_destroyed), window);
+ g_signal_connect (window->priv->keyboard_socket, "plug_added",
+ G_CALLBACK (keyboard_plug_added), window);
+ g_signal_connect (window->priv->keyboard_socket, "plug_removed",
+ G_CALLBACK (keyboard_plug_removed), window);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->keyboard_socket, FALSE, FALSE, 0);
+ gtk_socket_add_id (GTK_SOCKET (window->priv->keyboard_socket), id);
+}
+
+/* adapted from gspawn.c */
+static int
+wait_on_child (int pid)
+{
+ int status;
+
+wait_again:
+ if (waitpid (pid, &status, 0) < 0)
+ {
+ if (errno == EINTR)
+ {
+ goto wait_again;
+ }
+ else if (errno == ECHILD)
+ {
+ ; /* do nothing, child already reaped */
+ }
+ else
+ {
+ gs_debug ("waitpid () should not fail in 'GSWindow'");
+ }
+ }
+
+ return status;
+}
+
+static void
+kill_keyboard_command (GSWindow *window)
+{
+ if (window->priv->keyboard_pid > 0)
+ {
+ signal_pid (window->priv->keyboard_pid, SIGTERM);
+ }
+}
+
+static void
+kill_dialog_command (GSWindow *window)
+{
+ /* If a dialog is up we need to signal it
+ and wait on it */
+ if (window->priv->lock_pid > 0)
+ {
+ signal_pid (window->priv->lock_pid, SIGTERM);
+ }
+}
+
+static void
+keyboard_command_finish (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ /* send a signal just in case */
+ kill_keyboard_command (window);
+
+ gs_debug ("Keyboard finished");
+
+ if (window->priv->keyboard_pid > 0)
+ {
+ int exit_status;
+
+ exit_status = wait_on_child (window->priv->keyboard_pid);
+
+ g_spawn_close_pid (window->priv->keyboard_pid);
+ window->priv->keyboard_pid = 0;
+ }
+}
+
+static gboolean
+keyboard_command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSWindow *window)
+{
+ gboolean finished = FALSE;
+
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ {
+ guint32 id;
+ char c;
+ gs_debug ("keyboard command output: %s", line);
+ if (1 == sscanf (line, " %" G_GUINT32_FORMAT " %c", &id, &c))
+ {
+ create_keyboard_socket (window, id);
+ }
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ window->priv->keyboard_watch_id = 0;
+ keyboard_command_finish (window);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+embed_keyboard (GSWindow *window)
+{
+ gboolean res;
+
+ if (! window->priv->keyboard_enabled
+ || window->priv->keyboard_command == NULL)
+ return;
+
+ gs_debug ("Adding embedded keyboard widget");
+
+ /* FIXME: verify command is safe */
+
+ gs_debug ("Running command: %s", window->priv->keyboard_command);
+
+ res = spawn_on_window (window,
+ window->priv->keyboard_command,
+ &window->priv->keyboard_pid,
+ (GIOFunc)keyboard_command_watch,
+ window,
+ &window->priv->keyboard_watch_id);
+ if (! res)
+ {
+ gs_debug ("Could not start command: %s", window->priv->keyboard_command);
+ }
+}
+
+static void
+create_lock_socket (GSWindow *window,
+ guint32 id)
+{
+ window->priv->lock_socket = gtk_socket_new ();
+ window->priv->lock_box = gtk_alignment_new (0.5, 0.5, 0, 0);
+ gtk_widget_show (window->priv->lock_box);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->lock_box, TRUE, TRUE, 0);
+
+ gtk_container_add (GTK_CONTAINER (window->priv->lock_box), window->priv->lock_socket);
+
+ g_signal_connect (window->priv->lock_socket, "show",
+ G_CALLBACK (lock_socket_show), window);
+ g_signal_connect (window->priv->lock_socket, "destroy",
+ G_CALLBACK (lock_socket_destroyed), window);
+ g_signal_connect (window->priv->lock_socket, "plug_added",
+ G_CALLBACK (lock_plug_added), window);
+ g_signal_connect (window->priv->lock_socket, "plug_removed",
+ G_CALLBACK (lock_plug_removed), window);
+
+ gtk_socket_add_id (GTK_SOCKET (window->priv->lock_socket), id);
+
+ if (window->priv->keyboard_enabled)
+ {
+ embed_keyboard (window);
+ }
+}
+
+static void
+gs_window_dialog_finish (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Dialog finished");
+
+ /* make sure we finish the keyboard thing too */
+ keyboard_command_finish (window);
+
+ /* send a signal just in case */
+ kill_dialog_command (window);
+
+ if (window->priv->lock_pid > 0)
+ {
+ int exit_status;
+
+ exit_status = wait_on_child (window->priv->lock_pid);
+
+ g_spawn_close_pid (window->priv->lock_pid);
+ window->priv->lock_pid = 0;
+ }
+
+ /* remove events for the case were we failed to show socket */
+ remove_key_events (window);
+}
+
+static void
+maybe_kill_dialog (GSWindow *window)
+{
+ if (!window->priv->dialog_shake_in_progress
+ && window->priv->dialog_quit_requested
+ && window->priv->lock_pid > 0)
+ {
+ kill (window->priv->lock_pid, SIGTERM);
+ }
+}
+
+/* very rudimentary animation for indicating an auth failure */
+static void
+shake_dialog (GSWindow *window)
+{
+ int i;
+ guint left;
+ guint right;
+
+ window->priv->dialog_shake_in_progress = TRUE;
+
+ for (i = 0; i < 9; i++)
+ {
+ if (i % 2 == 0)
+ {
+ left = 30;
+ right = 0;
+ }
+ else
+ {
+ left = 0;
+ right = 30;
+ }
+
+ if (! window->priv->lock_box)
+ {
+ break;
+ }
+
+ gtk_alignment_set_padding (GTK_ALIGNMENT (window->priv->lock_box),
+ 0, 0,
+ left,
+ right);
+
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ g_usleep (10000);
+ }
+
+ window->priv->dialog_shake_in_progress = FALSE;
+ maybe_kill_dialog (window);
+}
+
+static void
+window_set_dialog_up (GSWindow *window,
+ gboolean dialog_up)
+{
+ if (window->priv->dialog_up == dialog_up)
+ {
+ return;
+ }
+
+ window->priv->dialog_up = dialog_up;
+ g_object_notify (G_OBJECT (window), "dialog-up");
+}
+
+static void
+popdown_dialog (GSWindow *window)
+{
+ gs_window_dialog_finish (window);
+
+ gtk_widget_show (window->priv->drawing_area);
+
+ gs_window_clear (window);
+ set_invisible_cursor (GTK_WIDGET (window)->window, TRUE);
+
+ window_set_dialog_up (window, FALSE);
+
+ /* reset the pointer positions */
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+
+ if (window->priv->lock_box != NULL)
+ {
+ gtk_container_remove (GTK_CONTAINER (window->priv->vbox), GTK_WIDGET (window->priv->lock_box));
+ window->priv->lock_box = NULL;
+ }
+
+ remove_popup_dialog_idle (window);
+ remove_command_watches (window);
+}
+
+static gboolean
+lock_command_watch (GIOChannel *source,
+ GIOCondition condition,
+ GSWindow *window)
+{
+ gboolean finished = FALSE;
+
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ if (condition & G_IO_IN)
+ {
+ GIOStatus status;
+ GError *error = NULL;
+ char *line;
+
+ line = NULL;
+ status = g_io_channel_read_line (source, &line, NULL, NULL, &error);
+
+ switch (status)
+ {
+ case G_IO_STATUS_NORMAL:
+ gs_debug ("command output: %s", line);
+
+ if (strstr (line, "WINDOW ID=") != NULL)
+ {
+ guint32 id;
+ char c;
+ if (1 == sscanf (line, " WINDOW ID= %" G_GUINT32_FORMAT " %c", &id, &c))
+ {
+ create_lock_socket (window, id);
+ }
+ }
+ else if (strstr (line, "NOTICE=") != NULL)
+ {
+ if (strstr (line, "NOTICE=AUTH FAILED") != NULL)
+ {
+ shake_dialog (window);
+ }
+ }
+ else if (strstr (line, "RESPONSE=") != NULL)
+ {
+ if (strstr (line, "RESPONSE=OK") != NULL)
+ {
+ gs_debug ("Got OK response");
+ window->priv->dialog_response = DIALOG_RESPONSE_OK;
+ }
+ else
+ {
+ gs_debug ("Got CANCEL response");
+ window->priv->dialog_response = DIALOG_RESPONSE_CANCEL;
+ }
+ finished = TRUE;
+ }
+ else if (strstr (line, "REQUEST QUIT") != NULL)
+ {
+ gs_debug ("Got request for quit");
+ window->priv->dialog_quit_requested = TRUE;
+ maybe_kill_dialog (window);
+ }
+ break;
+ case G_IO_STATUS_EOF:
+ finished = TRUE;
+ break;
+ case G_IO_STATUS_ERROR:
+ finished = TRUE;
+ gs_debug ("Error reading from child: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ case G_IO_STATUS_AGAIN:
+ default:
+ break;
+ }
+
+ g_free (line);
+ }
+ else if (condition & G_IO_HUP)
+ {
+ finished = TRUE;
+ }
+
+ if (finished)
+ {
+ popdown_dialog (window);
+
+ if (window->priv->dialog_response == DIALOG_RESPONSE_OK)
+ {
+ add_emit_deactivated_idle (window);
+ }
+
+ window->priv->lock_watch_id = 0;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+is_logout_enabled (GSWindow *window)
+{
+ double elapsed;
+
+ if (! window->priv->logout_enabled)
+ {
+ return FALSE;
+ }
+
+ if (! window->priv->logout_command)
+ {
+ return FALSE;
+ }
+
+ elapsed = g_timer_elapsed (window->priv->timer, NULL);
+
+ if (window->priv->logout_timeout < (elapsed * 1000))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+is_user_switch_enabled (GSWindow *window)
+{
+ return window->priv->user_switch_enabled;
+}
+
+static void
+popup_dialog (GSWindow *window)
+{
+ gboolean result;
+ char *tmp;
+ GString *command;
+
+ gs_debug ("Popping up dialog");
+
+ tmp = g_build_filename (LIBEXECDIR, "mate-screensaver-dialog", NULL);
+ command = g_string_new (tmp);
+ g_free (tmp);
+
+ if (is_logout_enabled (window))
+ {
+ command = g_string_append (command, " --enable-logout");
+ g_string_append_printf (command, " --logout-command='%s'", window->priv->logout_command);
+ }
+
+ if (window->priv->status_message)
+ {
+ char *quoted;
+
+ quoted = g_shell_quote (window->priv->status_message);
+ g_string_append_printf (command, " --status-message=%s", quoted);
+ g_free (quoted);
+ }
+
+ if (is_user_switch_enabled (window))
+ {
+ command = g_string_append (command, " --enable-switch");
+ }
+
+ if (gs_debug_enabled ())
+ {
+ command = g_string_append (command, " --verbose");
+ }
+
+ gtk_widget_hide (window->priv->drawing_area);
+
+ gs_window_clear_to_background_pixmap (window);
+
+ set_invisible_cursor (GTK_WIDGET (window)->window, FALSE);
+
+ window->priv->dialog_quit_requested = FALSE;
+ window->priv->dialog_shake_in_progress = FALSE;
+
+ result = spawn_on_window (window,
+ command->str,
+ &window->priv->lock_pid,
+ (GIOFunc)lock_command_watch,
+ window,
+ &window->priv->lock_watch_id);
+ if (! result)
+ {
+ gs_debug ("Could not start command: %s", command->str);
+ }
+
+ g_string_free (command, TRUE);
+}
+
+static gboolean
+popup_dialog_idle (GSWindow *window)
+{
+ popup_dialog (window);
+
+ window->priv->popup_dialog_idle_id = 0;
+
+ return FALSE;
+}
+
+void
+gs_window_request_unlock (GSWindow *window)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ gs_debug ("Requesting unlock");
+
+ if (! GTK_WIDGET_VISIBLE (GTK_WIDGET (window)))
+ {
+ gs_debug ("Request unlock but window is not visible!");
+ return;
+ }
+
+ if (window->priv->lock_watch_id > 0)
+ {
+ return;
+ }
+
+ if (! window->priv->lock_enabled)
+ {
+ add_emit_deactivated_idle (window);
+
+ return;
+ }
+
+ if (window->priv->popup_dialog_idle_id == 0)
+ {
+ add_popup_dialog_idle (window);
+ }
+
+ window_set_dialog_up (window, TRUE);
+}
+
+void
+gs_window_cancel_unlock_request (GSWindow *window)
+{
+ /* FIXME: This is a bit of a hammer approach...
+ * Maybe we should send a delete-event to
+ * the plug?
+ */
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ popdown_dialog (window);
+}
+
+void
+gs_window_set_lock_enabled (GSWindow *window,
+ gboolean lock_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->lock_enabled == lock_enabled)
+ {
+ return;
+ }
+
+ window->priv->lock_enabled = lock_enabled;
+ g_object_notify (G_OBJECT (window), "lock-enabled");
+}
+
+void
+gs_window_set_screen (GSWindow *window,
+ GdkScreen *screen)
+{
+
+ g_return_if_fail (GS_IS_WINDOW (window));
+ g_return_if_fail (GDK_IS_SCREEN (screen));
+
+ gtk_window_set_screen (GTK_WINDOW (window), screen);
+}
+
+GdkScreen *
+gs_window_get_screen (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), NULL);
+
+ return GTK_WINDOW (window)->screen;
+}
+
+void
+gs_window_set_keyboard_enabled (GSWindow *window,
+ gboolean enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->keyboard_enabled = enabled;
+}
+
+void
+gs_window_set_keyboard_command (GSWindow *window,
+ const char *command)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->keyboard_command);
+
+ if (command != NULL)
+ {
+ window->priv->keyboard_command = g_strdup (command);
+ }
+ else
+ {
+ window->priv->keyboard_command = NULL;
+ }
+}
+
+void
+gs_window_set_logout_enabled (GSWindow *window,
+ gboolean logout_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->logout_enabled = logout_enabled;
+}
+
+void
+gs_window_set_user_switch_enabled (GSWindow *window,
+ gboolean user_switch_enabled)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ window->priv->user_switch_enabled = user_switch_enabled;
+}
+
+void
+gs_window_set_logout_timeout (GSWindow *window,
+ glong logout_timeout)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (logout_timeout < 0)
+ {
+ window->priv->logout_timeout = 0;
+ }
+ else
+ {
+ window->priv->logout_timeout = logout_timeout;
+ }
+}
+
+void
+gs_window_set_logout_command (GSWindow *window,
+ const char *command)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->logout_command);
+
+ if (command)
+ {
+ window->priv->logout_command = g_strdup (command);
+ }
+ else
+ {
+ window->priv->logout_command = NULL;
+ }
+}
+
+void
+gs_window_set_status_message (GSWindow *window,
+ const char *status_message)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ g_free (window->priv->status_message);
+ window->priv->status_message = g_strdup (status_message);
+}
+
+void
+gs_window_set_monitor (GSWindow *window,
+ int monitor)
+{
+ g_return_if_fail (GS_IS_WINDOW (window));
+
+ if (window->priv->monitor == monitor)
+ {
+ return;
+ }
+
+ window->priv->monitor = monitor;
+
+ gtk_widget_queue_resize (GTK_WIDGET (window));
+
+ g_object_notify (G_OBJECT (window), "monitor");
+}
+
+int
+gs_window_get_monitor (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), -1);
+
+ return window->priv->monitor;
+}
+
+static void
+gs_window_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GSWindow *self;
+
+ self = GS_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOCK_ENABLED:
+ gs_window_set_lock_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ gs_window_set_keyboard_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ gs_window_set_keyboard_command (self, g_value_get_string (value));
+ break;
+ case PROP_LOGOUT_ENABLED:
+ gs_window_set_logout_enabled (self, g_value_get_boolean (value));
+ break;
+ case PROP_LOGOUT_COMMAND:
+ gs_window_set_logout_command (self, g_value_get_string (value));
+ break;
+ case PROP_STATUS_MESSAGE:
+ gs_window_set_status_message (self, g_value_get_string (value));
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ gs_window_set_logout_timeout (self, g_value_get_long (value));
+ break;
+ case PROP_MONITOR:
+ gs_window_set_monitor (self, g_value_get_int (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+gs_window_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GSWindow *self;
+
+ self = GS_WINDOW (object);
+
+ switch (prop_id)
+ {
+ case PROP_LOCK_ENABLED:
+ g_value_set_boolean (value, self->priv->lock_enabled);
+ break;
+ case PROP_KEYBOARD_ENABLED:
+ g_value_set_boolean (value, self->priv->keyboard_enabled);
+ break;
+ case PROP_KEYBOARD_COMMAND:
+ g_value_set_string (value, self->priv->keyboard_command);
+ break;
+ case PROP_LOGOUT_ENABLED:
+ g_value_set_boolean (value, self->priv->logout_enabled);
+ break;
+ case PROP_LOGOUT_COMMAND:
+ g_value_set_string (value, self->priv->logout_command);
+ break;
+ case PROP_STATUS_MESSAGE:
+ g_value_set_string (value, self->priv->status_message);
+ break;
+ case PROP_LOGOUT_TIMEOUT:
+ g_value_set_long (value, self->priv->logout_timeout);
+ break;
+ case PROP_MONITOR:
+ g_value_set_int (value, self->priv->monitor);
+ break;
+ case PROP_OBSCURED:
+ g_value_set_boolean (value, self->priv->obscured);
+ break;
+ case PROP_DIALOG_UP:
+ g_value_set_boolean (value, self->priv->dialog_up);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+queue_key_event (GSWindow *window,
+ GdkEventKey *event)
+{
+ /* Eat the first return, enter, escape, or space */
+ if (window->priv->key_events == NULL
+ && (event->keyval == GDK_Return
+ || event->keyval == GDK_KP_Enter
+ || event->keyval == GDK_Escape
+ || event->keyval == GDK_space))
+ {
+ return;
+ }
+
+ /* Only cache MAX_QUEUED_EVENTS key events. If there are any more than this then
+ something is wrong */
+ /* Don't queue keys that may cause focus navigation in the dialog */
+ if (g_list_length (window->priv->key_events) < MAX_QUEUED_EVENTS
+ && event->keyval != GDK_Tab
+ && event->keyval != GDK_Up
+ && event->keyval != GDK_Down)
+ {
+ window->priv->key_events = g_list_prepend (window->priv->key_events,
+ gdk_event_copy ((GdkEvent *)event));
+ }
+}
+
+static gboolean
+maybe_handle_activity (GSWindow *window)
+{
+ gboolean handled;
+
+ handled = FALSE;
+
+ /* if we already have a socket then don't bother */
+ if (! window->priv->lock_socket
+ && GTK_WIDGET_IS_SENSITIVE (GTK_WIDGET (window)))
+ {
+ g_signal_emit (window, signals [ACTIVITY], 0, &handled);
+ }
+
+ return handled;
+}
+
+static gboolean
+gs_window_real_key_press_event (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ /*g_message ("KEY PRESS state: %u keyval %u", event->state, event->keyval);*/
+
+ /* Ignore brightness keys */
+ if (event->hardware_keycode == 101 || event->hardware_keycode == 212)
+ {
+ gs_debug ("Ignoring brightness keys");
+ return TRUE;
+ }
+
+ maybe_handle_activity (GS_WINDOW (widget));
+
+ queue_key_event (GS_WINDOW (widget), event);
+
+ if (GTK_WIDGET_CLASS (gs_window_parent_class)->key_press_event)
+ {
+ GTK_WIDGET_CLASS (gs_window_parent_class)->key_press_event (widget, event);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gs_window_real_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ GSWindow *window;
+ gdouble distance;
+ gdouble min_distance;
+ gdouble min_percentage = 0.1;
+ GdkScreen *screen;
+
+ window = GS_WINDOW (widget);
+
+ screen = gs_window_get_screen (window);
+ min_distance = gdk_screen_get_width (screen) * min_percentage;
+
+ /* if the last position was not set then don't detect motion */
+ if (window->priv->last_x < 0 || window->priv->last_y < 0)
+ {
+ window->priv->last_x = event->x;
+ window->priv->last_y = event->y;
+
+ return FALSE;
+ }
+
+ /* just an approximate distance */
+ distance = MAX (ABS (window->priv->last_x - event->x),
+ ABS (window->priv->last_y - event->y));
+
+ if (distance > min_distance)
+ {
+ maybe_handle_activity (window);
+
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gs_window_real_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+ maybe_handle_activity (window);
+
+ return FALSE;
+}
+
+static gboolean
+gs_window_real_scroll_event (GtkWidget *widget,
+ GdkEventScroll *event)
+{
+ GSWindow *window;
+
+ window = GS_WINDOW (widget);
+ maybe_handle_activity (window);
+
+ return FALSE;
+}
+
+static void
+gs_window_real_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GSWindow *window;
+ GtkBin *bin;
+ GdkRectangle old_geometry;
+ int position_changed = FALSE;
+ int size_changed = FALSE;
+
+ window = GS_WINDOW (widget);
+ bin = GTK_BIN (widget);
+
+ if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
+ {
+ gtk_widget_size_request (bin->child, requisition);
+ }
+
+ old_geometry = window->priv->geometry;
+
+ update_geometry (window);
+
+ requisition->width = window->priv->geometry.width;
+ requisition->height = window->priv->geometry.height;
+
+ if (! GTK_WIDGET_REALIZED (widget))
+ {
+ return;
+ }
+
+ if (old_geometry.width != window->priv->geometry.width ||
+ old_geometry.height != window->priv->geometry.height)
+ {
+ size_changed = TRUE;
+ }
+
+ if (old_geometry.x != window->priv->geometry.x ||
+ old_geometry.y != window->priv->geometry.y)
+ {
+ position_changed = TRUE;
+ }
+
+ gs_window_move_resize_window (window, position_changed, size_changed);
+}
+
+static gboolean
+gs_window_real_grab_broken (GtkWidget *widget,
+ GdkEventGrabBroken *event)
+{
+ if (event->grab_window != NULL)
+ {
+ gs_debug ("Grab broken on window %X %s, new grab on window %X",
+ (guint32) GDK_WINDOW_XID (event->window),
+ event->keyboard ? "keyboard" : "pointer",
+ (guint32) GDK_WINDOW_XID (event->grab_window));
+ }
+ else
+ {
+ gs_debug ("Grab broken on window %X %s, new grab is outside application",
+ (guint32) GDK_WINDOW_XID (event->window),
+ event->keyboard ? "keyboard" : "pointer");
+ }
+
+ return FALSE;
+}
+
+gboolean
+gs_window_is_obscured (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ return window->priv->obscured;
+}
+
+gboolean
+gs_window_is_dialog_up (GSWindow *window)
+{
+ g_return_val_if_fail (GS_IS_WINDOW (window), FALSE);
+
+ return window->priv->dialog_up;
+}
+
+static void
+window_set_obscured (GSWindow *window,
+ gboolean obscured)
+{
+ if (window->priv->obscured == obscured)
+ {
+ return;
+ }
+
+ window->priv->obscured = obscured;
+ g_object_notify (G_OBJECT (window), "obscured");
+}
+
+static gboolean
+gs_window_real_visibility_notify_event (GtkWidget *widget,
+ GdkEventVisibility *event)
+{
+ switch (event->state)
+ {
+ case GDK_VISIBILITY_FULLY_OBSCURED:
+ window_set_obscured (GS_WINDOW (widget), TRUE);
+ break;
+ case GDK_VISIBILITY_PARTIAL:
+ break;
+ case GDK_VISIBILITY_UNOBSCURED:
+ window_set_obscured (GS_WINDOW (widget), FALSE);
+ break;
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+gs_window_class_init (GSWindowClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = gs_window_finalize;
+ object_class->get_property = gs_window_get_property;
+ object_class->set_property = gs_window_set_property;
+
+ widget_class->show = gs_window_real_show;
+ widget_class->hide = gs_window_real_hide;
+ widget_class->realize = gs_window_real_realize;
+ widget_class->unrealize = gs_window_real_unrealize;
+ widget_class->key_press_event = gs_window_real_key_press_event;
+ widget_class->motion_notify_event = gs_window_real_motion_notify_event;
+ widget_class->button_press_event = gs_window_real_button_press_event;
+ widget_class->scroll_event = gs_window_real_scroll_event;
+ widget_class->size_request = gs_window_real_size_request;
+ widget_class->grab_broken_event = gs_window_real_grab_broken;
+ widget_class->visibility_notify_event = gs_window_real_visibility_notify_event;
+
+ g_type_class_add_private (klass, sizeof (GSWindowPrivate));
+
+ signals [ACTIVITY] =
+ g_signal_new ("activity",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWindowClass, activity),
+ NULL,
+ NULL,
+ gs_marshal_BOOLEAN__VOID,
+ G_TYPE_BOOLEAN,
+ 0);
+ signals [DEACTIVATED] =
+ g_signal_new ("deactivated",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GSWindowClass, deactivated),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ g_object_class_install_property (object_class,
+ PROP_OBSCURED,
+ g_param_spec_boolean ("obscured",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_DIALOG_UP,
+ g_param_spec_boolean ("dialog-up",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_LOCK_ENABLED,
+ g_param_spec_boolean ("lock-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_ENABLED,
+ g_param_spec_boolean ("logout-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_TIMEOUT,
+ g_param_spec_long ("logout-timeout",
+ NULL,
+ NULL,
+ -1,
+ G_MAXLONG,
+ 0,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_LOGOUT_COMMAND,
+ g_param_spec_string ("logout-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_STATUS_MESSAGE,
+ g_param_spec_string ("status-message",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_KEYBOARD_ENABLED,
+ g_param_spec_boolean ("keyboard-enabled",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_KEYBOARD_COMMAND,
+ g_param_spec_string ("keyboard-command",
+ NULL,
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_MONITOR,
+ g_param_spec_int ("monitor",
+ "Xinerama monitor",
+ "The monitor (in terms of Xinerama) which the window is on",
+ 0,
+ G_MAXINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+}
+
+static void
+create_info_bar (GSWindow *window)
+{
+ window->priv->info_bar = gtk_info_bar_new ();
+ gtk_widget_set_no_show_all (window->priv->info_bar, TRUE);
+ gtk_box_pack_end (GTK_BOX (window->priv->vbox), window->priv->info_bar, FALSE, FALSE, 0);
+}
+
+static void
+gs_window_init (GSWindow *window)
+{
+ window->priv = GS_WINDOW_GET_PRIVATE (window);
+
+ window->priv->geometry.x = -1;
+ window->priv->geometry.y = -1;
+ window->priv->geometry.width = -1;
+ window->priv->geometry.height = -1;
+
+ window->priv->last_x = -1;
+ window->priv->last_y = -1;
+
+ gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
+
+ gtk_window_set_skip_taskbar_hint (GTK_WINDOW (window), TRUE);
+ gtk_window_set_skip_pager_hint (GTK_WINDOW (window), TRUE);
+
+ gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
+
+ gtk_window_fullscreen (GTK_WINDOW (window));
+
+ gtk_widget_set_events (GTK_WIDGET (window),
+ gtk_widget_get_events (GTK_WIDGET (window))
+ | GDK_POINTER_MOTION_MASK
+ | GDK_BUTTON_PRESS_MASK
+ | GDK_BUTTON_RELEASE_MASK
+ | GDK_KEY_PRESS_MASK
+ | GDK_KEY_RELEASE_MASK
+ | GDK_EXPOSURE_MASK
+ | GDK_VISIBILITY_NOTIFY_MASK
+ | GDK_ENTER_NOTIFY_MASK
+ | GDK_LEAVE_NOTIFY_MASK);
+
+ window->priv->vbox = gtk_vbox_new (FALSE, 12);
+ gtk_widget_show (window->priv->vbox);
+ gtk_container_add (GTK_CONTAINER (window), window->priv->vbox);
+
+ window->priv->drawing_area = gtk_drawing_area_new ();
+ gtk_widget_show (window->priv->drawing_area);
+ gtk_box_pack_start (GTK_BOX (window->priv->vbox), window->priv->drawing_area, TRUE, TRUE, 0);
+ create_info_bar (window);
+
+ force_no_pixmap_background (window->priv->drawing_area);
+}
+
+static void
+remove_command_watches (GSWindow *window)
+{
+ if (window->priv->lock_watch_id != 0)
+ {
+ g_source_remove (window->priv->lock_watch_id);
+ window->priv->lock_watch_id = 0;
+ }
+ if (window->priv->keyboard_watch_id != 0)
+ {
+ g_source_remove (window->priv->keyboard_watch_id);
+ window->priv->keyboard_watch_id = 0;
+ }
+}
+
+static void
+gs_window_finalize (GObject *object)
+{
+ GSWindow *window;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GS_IS_WINDOW (object));
+
+ window = GS_WINDOW (object);
+
+ g_return_if_fail (window->priv != NULL);
+
+ g_free (window->priv->logout_command);
+ g_free (window->priv->keyboard_command);
+
+ if (window->priv->info_bar_timer_id > 0)
+ {
+ g_source_remove (window->priv->info_bar_timer_id);
+ }
+
+ remove_watchdog_timer (window);
+ remove_popup_dialog_idle (window);
+
+ if (window->priv->timer)
+ {
+ g_timer_destroy (window->priv->timer);
+ }
+
+ remove_key_events (window);
+
+ remove_command_watches (window);
+
+ gs_window_dialog_finish (window);
+
+ if (window->priv->background_pixmap)
+ {
+ g_object_unref (window->priv->background_pixmap);
+ }
+
+ G_OBJECT_CLASS (gs_window_parent_class)->finalize (object);
+}
+
+GSWindow *
+gs_window_new (GdkScreen *screen,
+ int monitor,
+ gboolean lock_enabled)
+{
+ GObject *result;
+
+ result = g_object_new (GS_TYPE_WINDOW,
+ "type", GTK_WINDOW_POPUP,
+ "screen", screen,
+ "monitor", monitor,
+ "lock-enabled", lock_enabled,
+ NULL);
+
+ return GS_WINDOW (result);
+}
diff --git a/src/gs-window.h b/src/gs-window.h
new file mode 100644
index 0000000..9c8aee3
--- /dev/null
+++ b/src/gs-window.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_WINDOW_H
+#define __GS_WINDOW_H
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_WINDOW (gs_window_get_type ())
+#define GS_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_WINDOW, GSWindow))
+#define GS_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_WINDOW, GSWindowClass))
+#define GS_IS_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_WINDOW))
+#define GS_IS_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_WINDOW))
+#define GS_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_WINDOW, GSWindowClass))
+
+typedef struct GSWindowPrivate GSWindowPrivate;
+
+typedef struct
+{
+ GtkWindow window;
+
+ GSWindowPrivate *priv;
+} GSWindow;
+
+typedef struct
+{
+ GtkWindowClass parent_class;
+
+ gboolean (* activity) (GSWindow *window);
+ void (* deactivated) (GSWindow *window);
+ void (* dialog_up) (GSWindow *window);
+ void (* dialog_down) (GSWindow *window);
+} GSWindowClass;
+
+GType gs_window_get_type (void);
+
+gboolean gs_window_is_obscured (GSWindow *window);
+gboolean gs_window_is_dialog_up (GSWindow *window);
+
+void gs_window_set_screen (GSWindow *window,
+ GdkScreen *screen);
+GdkScreen * gs_window_get_screen (GSWindow *window);
+void gs_window_set_monitor (GSWindow *window,
+ int monitor);
+int gs_window_get_monitor (GSWindow *window);
+
+void gs_window_set_background_pixmap (GSWindow *window,
+ GdkPixmap *pixmap);
+void gs_window_set_lock_enabled (GSWindow *window,
+ gboolean lock_enabled);
+void gs_window_set_logout_enabled (GSWindow *window,
+ gboolean logout_enabled);
+void gs_window_set_keyboard_enabled (GSWindow *window,
+ gboolean enabled);
+void gs_window_set_keyboard_command (GSWindow *window,
+ const char *command);
+void gs_window_set_user_switch_enabled (GSWindow *window,
+ gboolean user_switch_enabled);
+void gs_window_set_logout_timeout (GSWindow *window,
+ glong timeout);
+void gs_window_set_logout_command (GSWindow *window,
+ const char *command);
+void gs_window_set_status_message (GSWindow *window,
+ const char *status_message);
+void gs_window_show_message (GSWindow *window,
+ const char *summary,
+ const char *body,
+ const char *icon);
+
+void gs_window_request_unlock (GSWindow *window);
+void gs_window_cancel_unlock_request (GSWindow *window);
+
+GSWindow * gs_window_new (GdkScreen *screen,
+ int monitor,
+ gboolean lock_enabled);
+void gs_window_show (GSWindow *window);
+void gs_window_destroy (GSWindow *window);
+GdkWindow * gs_window_get_gdk_window (GSWindow *window);
+GtkWidget * gs_window_get_drawing_area (GSWindow *window);
+void gs_window_clear (GSWindow *window);
+
+G_END_DECLS
+
+#endif /* __GS_WINDOW_H */
diff --git a/src/gs-window.h.orig b/src/gs-window.h.orig
new file mode 100644
index 0000000..39537fe
--- /dev/null
+++ b/src/gs-window.h.orig
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __GS_WINDOW_H
+#define __GS_WINDOW_H
+
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_WINDOW (gs_window_get_type ())
+#define GS_WINDOW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_WINDOW, GSWindow))
+#define GS_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GS_TYPE_WINDOW, GSWindowClass))
+#define GS_IS_WINDOW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_WINDOW))
+#define GS_IS_WINDOW_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GS_TYPE_WINDOW))
+#define GS_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GS_TYPE_WINDOW, GSWindowClass))
+
+typedef struct GSWindowPrivate GSWindowPrivate;
+
+typedef struct
+{
+ GtkWindow window;
+
+ GSWindowPrivate *priv;
+} GSWindow;
+
+typedef struct
+{
+ GtkWindowClass parent_class;
+
+ gboolean (* activity) (GSWindow *window);
+ void (* deactivated) (GSWindow *window);
+ void (* dialog_up) (GSWindow *window);
+ void (* dialog_down) (GSWindow *window);
+} GSWindowClass;
+
+GType gs_window_get_type (void);
+
+gboolean gs_window_is_obscured (GSWindow *window);
+gboolean gs_window_is_dialog_up (GSWindow *window);
+
+void gs_window_set_screen (GSWindow *window,
+ GdkScreen *screen);
+GdkScreen * gs_window_get_screen (GSWindow *window);
+void gs_window_set_monitor (GSWindow *window,
+ int monitor);
+int gs_window_get_monitor (GSWindow *window);
+
+void gs_window_set_background_pixmap (GSWindow *window,
+ GdkPixmap *pixmap);
+void gs_window_set_lock_enabled (GSWindow *window,
+ gboolean lock_enabled);
+void gs_window_set_logout_enabled (GSWindow *window,
+ gboolean logout_enabled);
+void gs_window_set_keyboard_enabled (GSWindow *window,
+ gboolean enabled);
+void gs_window_set_keyboard_command (GSWindow *window,
+ const char *command);
+void gs_window_set_user_switch_enabled (GSWindow *window,
+ gboolean user_switch_enabled);
+void gs_window_set_logout_timeout (GSWindow *window,
+ glong timeout);
+void gs_window_set_logout_command (GSWindow *window,
+ const char *command);
+void gs_window_set_status_message (GSWindow *window,
+ const char *status_message);
+void gs_window_show_message (GSWindow *window,
+ const char *summary,
+ const char *body,
+ const char *icon);
+
+void gs_window_request_unlock (GSWindow *window);
+void gs_window_cancel_unlock_request (GSWindow *window);
+
+GSWindow * gs_window_new (GdkScreen *screen,
+ int monitor,
+ gboolean lock_enabled);
+void gs_window_show (GSWindow *window);
+void gs_window_destroy (GSWindow *window);
+GdkWindow * gs_window_get_gdk_window (GSWindow *window);
+GtkWidget * gs_window_get_drawing_area (GSWindow *window);
+void gs_window_clear (GSWindow *window);
+
+G_END_DECLS
+
+#endif /* __GS_WINDOW_H */
diff --git a/src/mate-screensaver-command.c b/src/mate-screensaver-command.c
new file mode 100644
index 0000000..d6f0459
--- /dev/null
+++ b/src/mate-screensaver-command.c
@@ -0,0 +1,551 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <locale.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define GS_SERVICE "org.mate.ScreenSaver"
+#define GS_PATH "/org/mate/ScreenSaver"
+#define GS_INTERFACE "org.mate.ScreenSaver"
+
+/* this is for dbus < 0.3 */
+#if ((DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 30))
+#define dbus_bus_name_has_owner(connection, name, err) dbus_bus_service_exists(connection, name, err)
+#endif
+
+static gboolean do_quit = FALSE;
+static gboolean do_lock = FALSE;
+static gboolean do_cycle = FALSE;
+static gboolean do_activate = FALSE;
+static gboolean do_deactivate = FALSE;
+static gboolean do_version = FALSE;
+static gboolean do_poke = FALSE;
+static gboolean do_inhibit = FALSE;
+
+static gboolean do_query = FALSE;
+static gboolean do_time = FALSE;
+
+static char *inhibit_reason = NULL;
+static char *inhibit_application = NULL;
+
+static GOptionEntry entries [] =
+{
+ {
+ "exit", 0, 0, G_OPTION_ARG_NONE, &do_quit,
+ N_("Causes the screensaver to exit gracefully"), NULL
+ },
+ {
+ "query", 'q', 0, G_OPTION_ARG_NONE, &do_query,
+ N_("Query the state of the screensaver"), NULL
+ },
+ {
+ "time", 't', 0, G_OPTION_ARG_NONE, &do_time,
+ N_("Query the length of time the screensaver has been active"), NULL
+ },
+ {
+ "lock", 'l', 0, G_OPTION_ARG_NONE, &do_lock,
+ N_("Tells the running screensaver process to lock the screen immediately"), NULL
+ },
+ {
+ "cycle", 'c', 0, G_OPTION_ARG_NONE, &do_cycle,
+ N_("If the screensaver is active then switch to another graphics demo"), NULL
+ },
+ {
+ "activate", 'a', 0, G_OPTION_ARG_NONE, &do_activate,
+ N_("Turn the screensaver on (blank the screen)"), NULL
+ },
+ {
+ "deactivate", 'd', 0, G_OPTION_ARG_NONE, &do_deactivate,
+ N_("If the screensaver is active then deactivate it (un-blank the screen)"), NULL
+ },
+ {
+ "poke", 'p', 0, G_OPTION_ARG_NONE, &do_poke,
+ N_("Poke the running screensaver to simulate user activity"), NULL
+ },
+ {
+ "inhibit", 'i', 0, G_OPTION_ARG_NONE, &do_inhibit,
+ N_("Inhibit the screensaver from activating. Command blocks while inhibit is active."), NULL
+ },
+ {
+ "application-name", 'n', 0, G_OPTION_ARG_STRING, &inhibit_application,
+ N_("The calling application that is inhibiting the screensaver"), NULL
+ },
+ {
+ "reason", 'r', 0, G_OPTION_ARG_STRING, &inhibit_reason,
+ N_("The reason for inhibiting the screensaver"), NULL
+ },
+ {
+ "version", 'V', 0, G_OPTION_ARG_NONE, &do_version,
+ N_("Version of this application"), NULL
+ },
+ { NULL }
+};
+
+static GMainLoop *loop = NULL;
+
+static gboolean
+screensaver_is_running (DBusConnection *connection)
+{
+ DBusError error;
+ gboolean exists;
+
+ g_return_val_if_fail (connection != NULL, FALSE);
+
+ dbus_error_init (&error);
+ exists = dbus_bus_name_has_owner (connection, GS_SERVICE, &error);
+ if (dbus_error_is_set (&error))
+ dbus_error_free (&error);
+
+ return exists;
+}
+
+static DBusMessage *
+screensaver_send_message_inhibit (DBusConnection *connection,
+ const char *application,
+ const char *reason)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, "Inhibit");
+ if (message == NULL)
+ {
+ g_warning ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &application);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &reason);
+
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error))
+ {
+ g_warning ("%s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return reply;
+}
+
+static DBusMessage *
+screensaver_send_message_bool (DBusConnection *connection,
+ const char *name,
+ gboolean value)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, name);
+ if (message == NULL)
+ {
+ g_warning ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &value);
+
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error))
+ {
+ g_warning ("%s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return reply;
+}
+
+static DBusMessage *
+screensaver_send_message_void (DBusConnection *connection,
+ const char *name,
+ gboolean expect_reply)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, name);
+ if (message == NULL)
+ {
+ g_warning ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ if (! expect_reply)
+ {
+ if (!dbus_connection_send (connection, message, NULL))
+ g_warning ("could not send message");
+ reply = NULL;
+ }
+ else
+ {
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error))
+ {
+ g_warning ("%s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+ }
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return reply;
+}
+
+static char **
+get_string_from_iter (DBusMessageIter *iter,
+ int *num_elements)
+{
+ int count;
+ char **buffer;
+
+ if (num_elements != NULL)
+ {
+ *num_elements = 0;
+ }
+
+ count = 0;
+ buffer = (char **)malloc (sizeof (char *) * 8);
+
+ if (buffer == NULL)
+ {
+ goto oom;
+ }
+
+ buffer[0] = NULL;
+ while (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_STRING)
+ {
+ const char *value;
+ char *str;
+
+ if ((count % 8) == 0 && count != 0)
+ {
+ buffer = realloc (buffer, sizeof (char *) * (count + 8));
+ if (buffer == NULL)
+ {
+ goto oom;
+ }
+ }
+
+ dbus_message_iter_get_basic (iter, &value);
+ str = strdup (value);
+ if (str == NULL)
+ {
+ goto oom;
+ }
+
+ buffer[count] = str;
+
+ dbus_message_iter_next (iter);
+ count++;
+ }
+
+ if ((count % 8) == 0)
+ {
+ buffer = realloc (buffer, sizeof (char *) * (count + 1));
+ if (buffer == NULL)
+ {
+ goto oom;
+ }
+ }
+
+ buffer[count] = NULL;
+ if (num_elements != NULL)
+ {
+ *num_elements = count;
+ }
+ return buffer;
+
+oom:
+ g_debug ("%s %d : error allocating memory\n", __FILE__, __LINE__);
+ return NULL;
+
+}
+
+static gboolean
+do_command (DBusConnection *connection)
+{
+ DBusMessage *reply;
+
+ if (do_quit)
+ {
+ reply = screensaver_send_message_void (connection, "Quit", FALSE);
+ goto done;
+ }
+
+ if (do_query)
+ {
+ DBusMessageIter iter;
+ DBusMessageIter array;
+ dbus_bool_t v;
+
+ reply = screensaver_send_message_void (connection, "GetActive", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &v);
+ g_print (_("The screensaver is %s\n"), v ? _("active") : _("inactive"));
+
+ dbus_message_unref (reply);
+
+ reply = screensaver_send_message_void (connection, "GetInhibitors", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from screensaver.");
+ goto done;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_recurse (&iter, &array);
+
+ if (dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_INVALID)
+ {
+ g_print (_("The screensaver is not inhibited\n"));
+ }
+ else
+ {
+ char **inhibitors;
+ int i;
+ int num;
+
+ g_print (_("The screensaver is being inhibited by:\n"));
+ inhibitors = get_string_from_iter (&array, &num);
+ for (i = 0; i < num; i++)
+ {
+ g_print ("\t%s\n", inhibitors[i]);
+ }
+ g_strfreev (inhibitors);
+ }
+
+ dbus_message_unref (reply);
+ }
+
+ if (do_time)
+ {
+ DBusMessageIter iter;
+ dbus_bool_t v;
+ dbus_int32_t t;
+
+ reply = screensaver_send_message_void (connection, "GetActive", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &v);
+ dbus_message_unref (reply);
+
+ if (v)
+ {
+
+ reply = screensaver_send_message_void (connection, "GetActiveTime", TRUE);
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &t);
+ g_print (_("The screensaver has been active for %d seconds.\n"), t);
+
+ dbus_message_unref (reply);
+ }
+ else
+ {
+ g_print (_("The screensaver is not currently active.\n"));
+ }
+ }
+
+ if (do_lock)
+ {
+ reply = screensaver_send_message_void (connection, "Lock", FALSE);
+ }
+
+ if (do_cycle)
+ {
+ reply = screensaver_send_message_void (connection, "Cycle", FALSE);
+ }
+
+ if (do_poke)
+ {
+ reply = screensaver_send_message_void (connection, "SimulateUserActivity", FALSE);
+ }
+
+ if (do_activate)
+ {
+ reply = screensaver_send_message_bool (connection, "SetActive", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+ dbus_message_unref (reply);
+ }
+
+ if (do_deactivate)
+ {
+ reply = screensaver_send_message_bool (connection, "SetActive", FALSE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+ dbus_message_unref (reply);
+ }
+
+ if (do_inhibit)
+ {
+ reply = screensaver_send_message_inhibit (connection,
+ inhibit_application ? inhibit_application : "Unknown",
+ inhibit_reason ? inhibit_reason : "Unknown");
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+ dbus_message_unref (reply);
+
+ return FALSE;
+ }
+
+done:
+ g_main_loop_quit (loop);
+
+ return FALSE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ DBusConnection *connection;
+ DBusError dbus_error;
+ GOptionContext *context;
+ gboolean retval;
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+
+ if (setlocale (LC_ALL, "") == NULL)
+ g_warning ("Locale not understood by C library, internationalization will not work\n");
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+
+ g_option_context_free (context);
+
+ if (! retval)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (do_version)
+ {
+ g_print ("%s %s\n", argv [0], VERSION);
+ exit (1);
+ }
+
+ dbus_error_init (&dbus_error);
+ connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
+ if (! connection)
+ {
+ g_message ("Failed to connect to the D-BUS daemon: %s", dbus_error.message);
+ dbus_error_free (&dbus_error);
+ exit (1);
+ }
+
+ dbus_connection_setup_with_g_main (connection, NULL);
+
+ if (! screensaver_is_running (connection))
+ {
+ g_message ("Screensaver is not running!");
+ exit (1);
+ }
+
+ g_idle_add ((GSourceFunc)do_command, connection);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ return 0;
+}
diff --git a/src/mate-screensaver-command.c.orig b/src/mate-screensaver-command.c.orig
new file mode 100644
index 0000000..441f81e
--- /dev/null
+++ b/src/mate-screensaver-command.c.orig
@@ -0,0 +1,551 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <locale.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#define GS_SERVICE "org.mate.ScreenSaver"
+#define GS_PATH "/org/mate/ScreenSaver"
+#define GS_INTERFACE "org.mate.ScreenSaver"
+
+/* this is for dbus < 0.3 */
+#if ((DBUS_VERSION_MAJOR == 0) && (DBUS_VERSION_MINOR < 30))
+#define dbus_bus_name_has_owner(connection, name, err) dbus_bus_service_exists(connection, name, err)
+#endif
+
+static gboolean do_quit = FALSE;
+static gboolean do_lock = FALSE;
+static gboolean do_cycle = FALSE;
+static gboolean do_activate = FALSE;
+static gboolean do_deactivate = FALSE;
+static gboolean do_version = FALSE;
+static gboolean do_poke = FALSE;
+static gboolean do_inhibit = FALSE;
+
+static gboolean do_query = FALSE;
+static gboolean do_time = FALSE;
+
+static char *inhibit_reason = NULL;
+static char *inhibit_application = NULL;
+
+static GOptionEntry entries [] =
+{
+ {
+ "exit", 0, 0, G_OPTION_ARG_NONE, &do_quit,
+ N_("Causes the screensaver to exit gracefully"), NULL
+ },
+ {
+ "query", 'q', 0, G_OPTION_ARG_NONE, &do_query,
+ N_("Query the state of the screensaver"), NULL
+ },
+ {
+ "time", 't', 0, G_OPTION_ARG_NONE, &do_time,
+ N_("Query the length of time the screensaver has been active"), NULL
+ },
+ {
+ "lock", 'l', 0, G_OPTION_ARG_NONE, &do_lock,
+ N_("Tells the running screensaver process to lock the screen immediately"), NULL
+ },
+ {
+ "cycle", 'c', 0, G_OPTION_ARG_NONE, &do_cycle,
+ N_("If the screensaver is active then switch to another graphics demo"), NULL
+ },
+ {
+ "activate", 'a', 0, G_OPTION_ARG_NONE, &do_activate,
+ N_("Turn the screensaver on (blank the screen)"), NULL
+ },
+ {
+ "deactivate", 'd', 0, G_OPTION_ARG_NONE, &do_deactivate,
+ N_("If the screensaver is active then deactivate it (un-blank the screen)"), NULL
+ },
+ {
+ "poke", 'p', 0, G_OPTION_ARG_NONE, &do_poke,
+ N_("Poke the running screensaver to simulate user activity"), NULL
+ },
+ {
+ "inhibit", 'i', 0, G_OPTION_ARG_NONE, &do_inhibit,
+ N_("Inhibit the screensaver from activating. Command blocks while inhibit is active."), NULL
+ },
+ {
+ "application-name", 'n', 0, G_OPTION_ARG_STRING, &inhibit_application,
+ N_("The calling application that is inhibiting the screensaver"), NULL
+ },
+ {
+ "reason", 'r', 0, G_OPTION_ARG_STRING, &inhibit_reason,
+ N_("The reason for inhibiting the screensaver"), NULL
+ },
+ {
+ "version", 'V', 0, G_OPTION_ARG_NONE, &do_version,
+ N_("Version of this application"), NULL
+ },
+ { NULL }
+};
+
+static GMainLoop *loop = NULL;
+
+static gboolean
+screensaver_is_running (DBusConnection *connection)
+{
+ DBusError error;
+ gboolean exists;
+
+ g_return_val_if_fail (connection != NULL, FALSE);
+
+ dbus_error_init (&error);
+ exists = dbus_bus_name_has_owner (connection, GS_SERVICE, &error);
+ if (dbus_error_is_set (&error))
+ dbus_error_free (&error);
+
+ return exists;
+}
+
+static DBusMessage *
+screensaver_send_message_inhibit (DBusConnection *connection,
+ const char *application,
+ const char *reason)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, "Inhibit");
+ if (message == NULL)
+ {
+ g_warning ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &application);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &reason);
+
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error))
+ {
+ g_warning ("%s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return reply;
+}
+
+static DBusMessage *
+screensaver_send_message_bool (DBusConnection *connection,
+ const char *name,
+ gboolean value)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+ DBusMessageIter iter;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, name);
+ if (message == NULL)
+ {
+ g_warning ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ dbus_message_iter_init_append (message, &iter);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &value);
+
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error))
+ {
+ g_warning ("%s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return reply;
+}
+
+static DBusMessage *
+screensaver_send_message_void (DBusConnection *connection,
+ const char *name,
+ gboolean expect_reply)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError error;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ dbus_error_init (&error);
+
+ message = dbus_message_new_method_call (GS_SERVICE, GS_PATH, GS_INTERFACE, name);
+ if (message == NULL)
+ {
+ g_warning ("Couldn't allocate the dbus message");
+ return NULL;
+ }
+
+ if (! expect_reply)
+ {
+ if (!dbus_connection_send (connection, message, NULL))
+ g_warning ("could not send message");
+ reply = NULL;
+ }
+ else
+ {
+ reply = dbus_connection_send_with_reply_and_block (connection,
+ message,
+ -1, &error);
+ if (dbus_error_is_set (&error))
+ {
+ g_warning ("%s raised:\n %s\n\n", error.name, error.message);
+ reply = NULL;
+ }
+ }
+ dbus_connection_flush (connection);
+
+ dbus_message_unref (message);
+ dbus_error_free (&error);
+
+ return reply;
+}
+
+static char **
+get_string_from_iter (DBusMessageIter *iter,
+ int *num_elements)
+{
+ int count;
+ char **buffer;
+
+ if (num_elements != NULL)
+ {
+ *num_elements = 0;
+ }
+
+ count = 0;
+ buffer = (char **)malloc (sizeof (char *) * 8);
+
+ if (buffer == NULL)
+ {
+ goto oom;
+ }
+
+ buffer[0] = NULL;
+ while (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_STRING)
+ {
+ const char *value;
+ char *str;
+
+ if ((count % 8) == 0 && count != 0)
+ {
+ buffer = realloc (buffer, sizeof (char *) * (count + 8));
+ if (buffer == NULL)
+ {
+ goto oom;
+ }
+ }
+
+ dbus_message_iter_get_basic (iter, &value);
+ str = strdup (value);
+ if (str == NULL)
+ {
+ goto oom;
+ }
+
+ buffer[count] = str;
+
+ dbus_message_iter_next (iter);
+ count++;
+ }
+
+ if ((count % 8) == 0)
+ {
+ buffer = realloc (buffer, sizeof (char *) * (count + 1));
+ if (buffer == NULL)
+ {
+ goto oom;
+ }
+ }
+
+ buffer[count] = NULL;
+ if (num_elements != NULL)
+ {
+ *num_elements = count;
+ }
+ return buffer;
+
+oom:
+ g_debug ("%s %d : error allocating memory\n", __FILE__, __LINE__);
+ return NULL;
+
+}
+
+static gboolean
+do_command (DBusConnection *connection)
+{
+ DBusMessage *reply;
+
+ if (do_quit)
+ {
+ reply = screensaver_send_message_void (connection, "Quit", FALSE);
+ goto done;
+ }
+
+ if (do_query)
+ {
+ DBusMessageIter iter;
+ DBusMessageIter array;
+ dbus_bool_t v;
+
+ reply = screensaver_send_message_void (connection, "GetActive", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &v);
+ g_print (_("The screensaver is %s\n"), v ? _("active") : _("inactive"));
+
+ dbus_message_unref (reply);
+
+ reply = screensaver_send_message_void (connection, "GetInhibitors", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from screensaver.");
+ goto done;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_recurse (&iter, &array);
+
+ if (dbus_message_iter_get_arg_type (&array) == DBUS_TYPE_INVALID)
+ {
+ g_print (_("The screensaver is not inhibited\n"));
+ }
+ else
+ {
+ char **inhibitors;
+ int i;
+ int num;
+
+ g_print (_("The screensaver is being inhibited by:\n"));
+ inhibitors = get_string_from_iter (&array, &num);
+ for (i = 0; i < num; i++)
+ {
+ g_print ("\t%s\n", inhibitors[i]);
+ }
+ g_strfreev (inhibitors);
+ }
+
+ dbus_message_unref (reply);
+ }
+
+ if (do_time)
+ {
+ DBusMessageIter iter;
+ dbus_bool_t v;
+ dbus_int32_t t;
+
+ reply = screensaver_send_message_void (connection, "GetActive", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &v);
+ dbus_message_unref (reply);
+
+ if (v)
+ {
+
+ reply = screensaver_send_message_void (connection, "GetActiveTime", TRUE);
+ dbus_message_iter_init (reply, &iter);
+ dbus_message_iter_get_basic (&iter, &t);
+ g_print (_("The screensaver has been active for %d seconds.\n"), t);
+
+ dbus_message_unref (reply);
+ }
+ else
+ {
+ g_print (_("The screensaver is not currently active.\n"));
+ }
+ }
+
+ if (do_lock)
+ {
+ reply = screensaver_send_message_void (connection, "Lock", FALSE);
+ }
+
+ if (do_cycle)
+ {
+ reply = screensaver_send_message_void (connection, "Cycle", FALSE);
+ }
+
+ if (do_poke)
+ {
+ reply = screensaver_send_message_void (connection, "SimulateUserActivity", FALSE);
+ }
+
+ if (do_activate)
+ {
+ reply = screensaver_send_message_bool (connection, "SetActive", TRUE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+ dbus_message_unref (reply);
+ }
+
+ if (do_deactivate)
+ {
+ reply = screensaver_send_message_bool (connection, "SetActive", FALSE);
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+ dbus_message_unref (reply);
+ }
+
+ if (do_inhibit)
+ {
+ reply = screensaver_send_message_inhibit (connection,
+ inhibit_application ? inhibit_application : "Unknown",
+ inhibit_reason ? inhibit_reason : "Unknown");
+ if (! reply)
+ {
+ g_message ("Did not receive a reply from the screensaver.");
+ goto done;
+ }
+ dbus_message_unref (reply);
+
+ return FALSE;
+ }
+
+done:
+ g_main_loop_quit (loop);
+
+ return FALSE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ DBusConnection *connection;
+ DBusError dbus_error;
+ GOptionContext *context;
+ gboolean retval;
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+
+ if (setlocale (LC_ALL, "") == NULL)
+ g_warning ("Locale not understood by C library, internationalization will not work\n");
+
+ context = g_option_context_new (NULL);
+ g_option_context_add_main_entries (context, entries, NULL);
+ retval = g_option_context_parse (context, &argc, &argv, &error);
+
+ g_option_context_free (context);
+
+ if (! retval)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (do_version)
+ {
+ g_print ("%s %s\n", argv [0], VERSION);
+ exit (1);
+ }
+
+ dbus_error_init (&dbus_error);
+ connection = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error);
+ if (! connection)
+ {
+ g_message ("Failed to connect to the D-BUS daemon: %s", dbus_error.message);
+ dbus_error_free (&dbus_error);
+ exit (1);
+ }
+
+ dbus_connection_setup_with_g_main (connection, NULL);
+
+ if (! screensaver_is_running (connection))
+ {
+ g_message ("Screensaver is not running!");
+ exit (1);
+ }
+
+ g_idle_add ((GSourceFunc)do_command, connection);
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ return 0;
+}
diff --git a/src/mate-screensaver-dialog.c b/src/mate-screensaver-dialog.c
new file mode 100644
index 0000000..d698159
--- /dev/null
+++ b/src/mate-screensaver-dialog.c
@@ -0,0 +1,665 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-lock-plug.h"
+
+#include "gs-auth.h"
+#include "setuid.h"
+
+#include "gs-debug.h"
+
+#define MAX_FAILURES 5
+
+static gboolean verbose = FALSE;
+static gboolean show_version = FALSE;
+static gboolean enable_logout = FALSE;
+static gboolean enable_switch = FALSE;
+static char *logout_command = NULL;
+static char *status_message = NULL;
+static char *away_message = NULL;
+
+static GOptionEntry entries [] =
+{
+ {
+ "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose,
+ N_("Show debugging output"), NULL
+ },
+ {
+ "version", 0, 0, G_OPTION_ARG_NONE, &show_version,
+ N_("Version of this application"), NULL
+ },
+ {
+ "enable-logout", 0, 0, G_OPTION_ARG_NONE, &enable_logout,
+ N_("Show the logout button"), NULL
+ },
+ {
+ "logout-command", 0, 0, G_OPTION_ARG_STRING, &logout_command,
+ N_("Command to invoke from the logout button"), NULL
+ },
+ {
+ "enable-switch", 0, 0, G_OPTION_ARG_NONE, &enable_switch,
+ N_("Show the switch user button"), NULL
+ },
+ {
+ "status-message", 0, 0, G_OPTION_ARG_STRING, &status_message,
+ N_("Message to show in the dialog"), N_("MESSAGE")
+ },
+ {
+ "away-message", 0, 0, G_OPTION_ARG_STRING, &away_message,
+ N_("Not used"), N_("MESSAGE")
+ },
+ { NULL }
+};
+
+static char *
+get_id_string (GtkWidget *widget)
+{
+ char *id = NULL;
+
+ g_return_val_if_fail (widget != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ id = g_strdup_printf ("%" G_GUINT32_FORMAT,
+ (guint32) GDK_WINDOW_XID (widget->window));
+ return id;
+}
+
+static gboolean
+print_id (GtkWidget *widget)
+{
+ char *id;
+
+ gs_profile_start (NULL);
+
+ id = get_id_string (widget);
+ printf ("WINDOW ID=%s\n", id);
+ fflush (stdout);
+
+ gs_profile_end (NULL);
+
+ g_free (id);
+
+ return FALSE;
+}
+
+static void
+response_cancel (void)
+{
+ printf ("RESPONSE=CANCEL\n");
+ fflush (stdout);
+}
+
+static void
+response_ok (void)
+{
+ printf ("RESPONSE=OK\n");
+ fflush (stdout);
+}
+
+static gboolean
+quit_response_ok (void)
+{
+ response_ok ();
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+quit_response_cancel (void)
+{
+ response_cancel ();
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static void
+response_lock_init_failed (void)
+{
+ /* if we fail to lock then we should drop the dialog */
+ response_ok ();
+}
+
+static char *
+request_response (GSLockPlug *plug,
+ const char *prompt,
+ gboolean visible)
+{
+ int response;
+ char *text;
+
+ gs_lock_plug_set_sensitive (plug, TRUE);
+ gs_lock_plug_enable_prompt (plug, prompt, visible);
+ response = gs_lock_plug_run (plug);
+
+ gs_debug ("got response: %d", response);
+
+ text = NULL;
+ if (response == GS_LOCK_PLUG_RESPONSE_OK)
+ {
+ gs_lock_plug_get_text (plug, &text);
+ }
+ gs_lock_plug_disable_prompt (plug);
+
+ return text;
+}
+
+/* Adapted from MDM2 daemon/verify-pam.c on 2006-06-13 */
+static const char *
+maybe_translate_message (const char *msg)
+{
+ char *s;
+ const char *ret;
+ static GHashTable *hash = NULL;
+
+ if (hash == NULL)
+ {
+ /* Here we come with some fairly standard messages so that
+ we have as much as possible translated. Should really be
+ translated in pam I suppose. This way we can "change"
+ some of these messages to be more sane. */
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+ /* login: is whacked always translate to Username: */
+ g_hash_table_insert (hash, "login:", _("Username:"));
+ g_hash_table_insert (hash, "Username:", _("Username:"));
+ g_hash_table_insert (hash, "username:", _("Username:"));
+ g_hash_table_insert (hash, "Password:", _("Password:"));
+ g_hash_table_insert (hash, "password:", _("Password:"));
+ g_hash_table_insert (hash, "You are required to change your password immediately (password aged)", _("You are required to change your password immediately (password aged)"));
+ g_hash_table_insert (hash, "You are required to change your password immediately (root enforced)", _("You are required to change your password immediately (root enforced)"));
+ g_hash_table_insert (hash, "Your account has expired; please contact your system administrator", _("Your account has expired; please contact your system administrator"));
+ g_hash_table_insert (hash, "No password supplied", _("No password supplied"));
+ g_hash_table_insert (hash, "Password unchanged", _("Password unchanged"));
+ g_hash_table_insert (hash, "Can not get username", _("Can not get username"));
+ g_hash_table_insert (hash, "Retype new UNIX password:", _("Retype new UNIX password:"));
+ g_hash_table_insert (hash, "Enter new UNIX password:", _("Enter new UNIX password:"));
+ g_hash_table_insert (hash, "(current) UNIX password:", _("(current) UNIX password:"));
+ g_hash_table_insert (hash, "Error while changing NIS password.", _("Error while changing NIS password."));
+ g_hash_table_insert (hash, "You must choose a longer password", _("You must choose a longer password"));
+ g_hash_table_insert (hash, "Password has been already used. Choose another.", _("Password has been already used. Choose another."));
+ g_hash_table_insert (hash, "You must wait longer to change your password", _("You must wait longer to change your password"));
+ g_hash_table_insert (hash, "Sorry, passwords do not match", _("Sorry, passwords do not match"));
+ /* FIXME: what about messages which have some variables in them, perhaps try to do those as well */
+ }
+
+ s = g_strstrip (g_strdup (msg));
+ ret = g_hash_table_lookup (hash, s);
+ g_free (s);
+
+ if (ret != NULL)
+ {
+ return ret;
+ }
+ else
+ {
+ return msg;
+ }
+}
+
+static gboolean
+auth_message_handler (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data)
+{
+ gboolean ret;
+ GSLockPlug *plug;
+ const char *message;
+
+ plug = GS_LOCK_PLUG (data);
+
+ gs_profile_start (NULL);
+ gs_debug ("Got message style %d: '%s'", style, msg);
+
+ gtk_widget_show (GTK_WIDGET (plug));
+ gs_lock_plug_set_ready (plug);
+
+ ret = TRUE;
+ *response = NULL;
+ message = maybe_translate_message (msg);
+
+ switch (style)
+ {
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
+ if (msg != NULL)
+ {
+ char *resp;
+ resp = request_response (plug, message, TRUE);
+ *response = resp;
+ }
+ break;
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
+ if (msg != NULL)
+ {
+ char *resp;
+ resp = request_response (plug, message, FALSE);
+ *response = resp;
+ }
+ break;
+ case GS_AUTH_MESSAGE_ERROR_MSG:
+ gs_lock_plug_show_message (plug, message);
+ break;
+ case GS_AUTH_MESSAGE_TEXT_INFO:
+ gs_lock_plug_show_message (plug, message);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (*response == NULL)
+ {
+ gs_debug ("Got no response");
+ ret = FALSE;
+ }
+ else
+ {
+ gs_lock_plug_show_message (plug, _("Checking..."));
+ gs_lock_plug_set_sensitive (plug, FALSE);
+ }
+
+ /* we may have pending events that should be processed before continuing back into PAM */
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ gs_lock_plug_set_busy (plug);
+ gs_profile_end (NULL);
+
+ return ret;
+}
+
+static gboolean
+reset_idle_cb (GSLockPlug *plug)
+{
+ gs_lock_plug_set_sensitive (plug, TRUE);
+ gs_lock_plug_show_message (plug, NULL);
+
+ return FALSE;
+}
+
+static gboolean
+do_auth_check (GSLockPlug *plug)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+
+ gs_lock_plug_disable_prompt (plug);
+ gs_lock_plug_set_busy (plug);
+ res = gs_auth_verify_user (g_get_user_name (), g_getenv ("DISPLAY"), auth_message_handler, plug, &error);
+
+ gs_debug ("Verify user returned: %s", res ? "TRUE" : "FALSE");
+ if (! res)
+ {
+ if (error != NULL)
+ {
+ gs_debug ("Verify user returned error: %s", error->message);
+ gs_lock_plug_show_message (plug, error->message);
+ }
+ else
+ {
+ gs_lock_plug_show_message (plug, _("Authentication failed."));
+ }
+
+ printf ("NOTICE=AUTH FAILED\n");
+ fflush (stdout);
+
+ if (error != NULL)
+ {
+ g_error_free (error);
+ }
+ }
+
+ return res;
+}
+
+static void
+response_cb (GSLockPlug *plug,
+ gint response_id)
+{
+ if ((response_id == GS_LOCK_PLUG_RESPONSE_CANCEL) ||
+ (response_id == GTK_RESPONSE_DELETE_EVENT))
+ {
+ quit_response_cancel ();
+ }
+}
+
+static gboolean
+response_request_quit (void)
+{
+ printf ("REQUEST QUIT\n");
+ fflush (stdout);
+ return FALSE;
+}
+
+static gboolean
+quit_timeout_cb (gpointer data)
+{
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+auth_check_idle (GSLockPlug *plug)
+{
+ gboolean res;
+ gboolean again;
+ static guint loop_counter = 0;
+
+ again = TRUE;
+ res = do_auth_check (plug);
+
+ if (res)
+ {
+ again = FALSE;
+ g_idle_add ((GSourceFunc)quit_response_ok, NULL);
+ }
+ else
+ {
+ loop_counter++;
+
+ if (loop_counter < MAX_FAILURES)
+ {
+ gs_debug ("Authentication failed, retrying (%u)", loop_counter);
+ g_timeout_add (3000, (GSourceFunc)reset_idle_cb, plug);
+ }
+ else
+ {
+ gs_debug ("Authentication failed, quitting (max failures)");
+ again = FALSE;
+ /* Don't quit immediately, but rather request that mate-screensaver
+ * terminates us after it has finished the dialog shake. Time out
+ * after 5 seconds and quit anyway if this doesn't happen though */
+ g_idle_add ((GSourceFunc)response_request_quit, NULL);
+ g_timeout_add (5000, (GSourceFunc)quit_timeout_cb, NULL);
+ }
+ }
+
+ return again;
+}
+
+static void
+show_cb (GtkWidget *widget,
+ gpointer data)
+{
+ print_id (widget);
+}
+
+static gboolean
+popup_dialog_idle (void)
+{
+ GtkWidget *widget;
+
+ gs_profile_start (NULL);
+
+ widget = gs_lock_plug_new ();
+
+ if (enable_logout)
+ {
+ g_object_set (widget, "logout-enabled", TRUE, NULL);
+ }
+
+ if (logout_command)
+ {
+ g_object_set (widget, "logout-command", logout_command, NULL);
+ }
+
+ if (enable_switch)
+ {
+ g_object_set (widget, "switch-enabled", TRUE, NULL);
+ }
+
+ if (status_message)
+ {
+ g_object_set (widget, "status-message", status_message, NULL);
+ }
+
+ g_signal_connect (GS_LOCK_PLUG (widget), "response", G_CALLBACK (response_cb), NULL);
+ g_signal_connect (widget, "show", G_CALLBACK (show_cb), NULL);
+
+ gtk_widget_realize (widget);
+
+ g_idle_add ((GSourceFunc)auth_check_idle, widget);
+
+ gs_profile_end (NULL);
+
+ return FALSE;
+}
+
+
+/*
+ * Copyright (c) 1991-2004 Jamie Zawinski <[email protected]>
+ * Copyright (c) 2005 William Jon McCann <[email protected]>
+ *
+ * Initializations that potentially take place as a priveleged user:
+ If the executable is setuid root, then these initializations
+ are run as root, before discarding privileges.
+*/
+static gboolean
+privileged_initialization (int *argc,
+ char **argv,
+ gboolean verbose)
+{
+ gboolean ret;
+ char *nolock_reason;
+ char *orig_uid;
+ char *uid_message;
+
+ gs_profile_start (NULL);
+
+#ifndef NO_LOCKING
+ /* before hack_uid () for proper permissions */
+ gs_auth_priv_init ();
+#endif /* NO_LOCKING */
+
+ ret = hack_uid (&nolock_reason,
+ &orig_uid,
+ &uid_message);
+
+ if (nolock_reason)
+ {
+ g_debug ("Locking disabled: %s", nolock_reason);
+ }
+
+ if (uid_message && verbose)
+ {
+ g_print ("Modified UID: %s", uid_message);
+ }
+
+ g_free (nolock_reason);
+ g_free (orig_uid);
+ g_free (uid_message);
+
+ gs_profile_end (NULL);
+
+ return ret;
+}
+
+
+/*
+ * Copyright (c) 1991-2004 Jamie Zawinski <[email protected]>
+ * Copyright (c) 2005 William Jon McCann <[email protected]>
+ *
+ * Figure out what locking mechanisms are supported.
+ */
+static gboolean
+lock_initialization (int *argc,
+ char **argv,
+ char **nolock_reason,
+ gboolean verbose)
+{
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = NULL;
+ }
+
+#ifdef NO_LOCKING
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("not compiled with locking support");
+ }
+
+ return FALSE;
+#else /* !NO_LOCKING */
+
+ /* Finish initializing locking, now that we're out of privileged code. */
+ if (! gs_auth_init ())
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("error getting password");
+ }
+
+ return FALSE;
+ }
+
+ /* If locking is currently enabled, but the environment indicates that
+ we have been launched as MDM's "Background" program, then disable
+ locking just in case.
+ */
+ if (getenv ("RUNNING_UNDER_MDM"))
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("running under MDM");
+ }
+
+ return FALSE;
+ }
+
+ /* If the server is XDarwin (MacOS X) then disable locking.
+ (X grabs only affect X programs, so you can use Command-Tab
+ to bring any other Mac program to the front, e.g., Terminal.)
+ */
+ {
+ gboolean macos = FALSE;
+
+#ifdef __APPLE__
+ /* Disable locking if *running* on Apple hardware, since we have no
+ reliable way to determine whether the server is running on MacOS.
+ Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
+ but I'm not really sure about that.
+ */
+ macos = TRUE;
+#endif
+
+ if (macos)
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("Cannot lock securely on MacOS X");
+ }
+
+ return FALSE;
+ }
+ }
+
+#endif /* NO_LOCKING */
+
+ return TRUE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+ char *nolock_reason = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! g_thread_supported ())
+ {
+ g_thread_init (NULL);
+ }
+
+ g_type_init ();
+
+ gs_profile_start (NULL);
+
+ if (! privileged_initialization (&argc, argv, verbose))
+ {
+ response_lock_init_failed ();
+ exit (1);
+ }
+
+ error = NULL;
+ if (! gtk_init_with_args (&argc, &argv, NULL, entries, NULL, &error))
+ {
+ if (error != NULL)
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ }
+ exit (1);
+ }
+
+ if (show_version)
+ {
+ g_print ("%s %s\n", argv [0], VERSION);
+ exit (1);
+ }
+
+ if (! lock_initialization (&argc, argv, &nolock_reason, verbose))
+ {
+ if (nolock_reason != NULL)
+ {
+ g_debug ("Screen locking disabled: %s", nolock_reason);
+ g_free (nolock_reason);
+ }
+ response_lock_init_failed ();
+ exit (1);
+ }
+
+ gs_debug_init (verbose, FALSE);
+
+ g_idle_add ((GSourceFunc)popup_dialog_idle, NULL);
+
+ gtk_main ();
+
+ gs_profile_end (NULL);
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/mate-screensaver-dialog.c.orig b/src/mate-screensaver-dialog.c.orig
new file mode 100644
index 0000000..9c7f143
--- /dev/null
+++ b/src/mate-screensaver-dialog.c.orig
@@ -0,0 +1,665 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-lock-plug.h"
+
+#include "gs-auth.h"
+#include "setuid.h"
+
+#include "gs-debug.h"
+
+#define MAX_FAILURES 5
+
+static gboolean verbose = FALSE;
+static gboolean show_version = FALSE;
+static gboolean enable_logout = FALSE;
+static gboolean enable_switch = FALSE;
+static char *logout_command = NULL;
+static char *status_message = NULL;
+static char *away_message = NULL;
+
+static GOptionEntry entries [] =
+{
+ {
+ "verbose", 0, 0, G_OPTION_ARG_NONE, &verbose,
+ N_("Show debugging output"), NULL
+ },
+ {
+ "version", 0, 0, G_OPTION_ARG_NONE, &show_version,
+ N_("Version of this application"), NULL
+ },
+ {
+ "enable-logout", 0, 0, G_OPTION_ARG_NONE, &enable_logout,
+ N_("Show the logout button"), NULL
+ },
+ {
+ "logout-command", 0, 0, G_OPTION_ARG_STRING, &logout_command,
+ N_("Command to invoke from the logout button"), NULL
+ },
+ {
+ "enable-switch", 0, 0, G_OPTION_ARG_NONE, &enable_switch,
+ N_("Show the switch user button"), NULL
+ },
+ {
+ "status-message", 0, 0, G_OPTION_ARG_STRING, &status_message,
+ N_("Message to show in the dialog"), N_("MESSAGE")
+ },
+ {
+ "away-message", 0, 0, G_OPTION_ARG_STRING, &away_message,
+ N_("Not used"), N_("MESSAGE")
+ },
+ { NULL }
+};
+
+static char *
+get_id_string (GtkWidget *widget)
+{
+ char *id = NULL;
+
+ g_return_val_if_fail (widget != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ id = g_strdup_printf ("%" G_GUINT32_FORMAT,
+ (guint32) GDK_WINDOW_XID (widget->window));
+ return id;
+}
+
+static gboolean
+print_id (GtkWidget *widget)
+{
+ char *id;
+
+ gs_profile_start (NULL);
+
+ id = get_id_string (widget);
+ printf ("WINDOW ID=%s\n", id);
+ fflush (stdout);
+
+ gs_profile_end (NULL);
+
+ g_free (id);
+
+ return FALSE;
+}
+
+static void
+response_cancel (void)
+{
+ printf ("RESPONSE=CANCEL\n");
+ fflush (stdout);
+}
+
+static void
+response_ok (void)
+{
+ printf ("RESPONSE=OK\n");
+ fflush (stdout);
+}
+
+static gboolean
+quit_response_ok (void)
+{
+ response_ok ();
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+quit_response_cancel (void)
+{
+ response_cancel ();
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static void
+response_lock_init_failed (void)
+{
+ /* if we fail to lock then we should drop the dialog */
+ response_ok ();
+}
+
+static char *
+request_response (GSLockPlug *plug,
+ const char *prompt,
+ gboolean visible)
+{
+ int response;
+ char *text;
+
+ gs_lock_plug_set_sensitive (plug, TRUE);
+ gs_lock_plug_enable_prompt (plug, prompt, visible);
+ response = gs_lock_plug_run (plug);
+
+ gs_debug ("got response: %d", response);
+
+ text = NULL;
+ if (response == GS_LOCK_PLUG_RESPONSE_OK)
+ {
+ gs_lock_plug_get_text (plug, &text);
+ }
+ gs_lock_plug_disable_prompt (plug);
+
+ return text;
+}
+
+/* Adapted from MDM2 daemon/verify-pam.c on 2006-06-13 */
+static const char *
+maybe_translate_message (const char *msg)
+{
+ char *s;
+ const char *ret;
+ static GHashTable *hash = NULL;
+
+ if (hash == NULL)
+ {
+ /* Here we come with some fairly standard messages so that
+ we have as much as possible translated. Should really be
+ translated in pam I suppose. This way we can "change"
+ some of these messages to be more sane. */
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
+ /* login: is whacked always translate to Username: */
+ g_hash_table_insert (hash, "login:", _("Username:"));
+ g_hash_table_insert (hash, "Username:", _("Username:"));
+ g_hash_table_insert (hash, "username:", _("Username:"));
+ g_hash_table_insert (hash, "Password:", _("Password:"));
+ g_hash_table_insert (hash, "password:", _("Password:"));
+ g_hash_table_insert (hash, "You are required to change your password immediately (password aged)", _("You are required to change your password immediately (password aged)"));
+ g_hash_table_insert (hash, "You are required to change your password immediately (root enforced)", _("You are required to change your password immediately (root enforced)"));
+ g_hash_table_insert (hash, "Your account has expired; please contact your system administrator", _("Your account has expired; please contact your system administrator"));
+ g_hash_table_insert (hash, "No password supplied", _("No password supplied"));
+ g_hash_table_insert (hash, "Password unchanged", _("Password unchanged"));
+ g_hash_table_insert (hash, "Can not get username", _("Can not get username"));
+ g_hash_table_insert (hash, "Retype new UNIX password:", _("Retype new UNIX password:"));
+ g_hash_table_insert (hash, "Enter new UNIX password:", _("Enter new UNIX password:"));
+ g_hash_table_insert (hash, "(current) UNIX password:", _("(current) UNIX password:"));
+ g_hash_table_insert (hash, "Error while changing NIS password.", _("Error while changing NIS password."));
+ g_hash_table_insert (hash, "You must choose a longer password", _("You must choose a longer password"));
+ g_hash_table_insert (hash, "Password has been already used. Choose another.", _("Password has been already used. Choose another."));
+ g_hash_table_insert (hash, "You must wait longer to change your password", _("You must wait longer to change your password"));
+ g_hash_table_insert (hash, "Sorry, passwords do not match", _("Sorry, passwords do not match"));
+ /* FIXME: what about messages which have some variables in them, perhaps try to do those as well */
+ }
+
+ s = g_strstrip (g_strdup (msg));
+ ret = g_hash_table_lookup (hash, s);
+ g_free (s);
+
+ if (ret != NULL)
+ {
+ return ret;
+ }
+ else
+ {
+ return msg;
+ }
+}
+
+static gboolean
+auth_message_handler (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data)
+{
+ gboolean ret;
+ GSLockPlug *plug;
+ const char *message;
+
+ plug = GS_LOCK_PLUG (data);
+
+ gs_profile_start (NULL);
+ gs_debug ("Got message style %d: '%s'", style, msg);
+
+ gtk_widget_show (GTK_WIDGET (plug));
+ gs_lock_plug_set_ready (plug);
+
+ ret = TRUE;
+ *response = NULL;
+ message = maybe_translate_message (msg);
+
+ switch (style)
+ {
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
+ if (msg != NULL)
+ {
+ char *resp;
+ resp = request_response (plug, message, TRUE);
+ *response = resp;
+ }
+ break;
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
+ if (msg != NULL)
+ {
+ char *resp;
+ resp = request_response (plug, message, FALSE);
+ *response = resp;
+ }
+ break;
+ case GS_AUTH_MESSAGE_ERROR_MSG:
+ gs_lock_plug_show_message (plug, message);
+ break;
+ case GS_AUTH_MESSAGE_TEXT_INFO:
+ gs_lock_plug_show_message (plug, message);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (*response == NULL)
+ {
+ gs_debug ("Got no response");
+ ret = FALSE;
+ }
+ else
+ {
+ gs_lock_plug_show_message (plug, _("Checking..."));
+ gs_lock_plug_set_sensitive (plug, FALSE);
+ }
+
+ /* we may have pending events that should be processed before continuing back into PAM */
+ while (gtk_events_pending ())
+ {
+ gtk_main_iteration ();
+ }
+
+ gs_lock_plug_set_busy (plug);
+ gs_profile_end (NULL);
+
+ return ret;
+}
+
+static gboolean
+reset_idle_cb (GSLockPlug *plug)
+{
+ gs_lock_plug_set_sensitive (plug, TRUE);
+ gs_lock_plug_show_message (plug, NULL);
+
+ return FALSE;
+}
+
+static gboolean
+do_auth_check (GSLockPlug *plug)
+{
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+
+ gs_lock_plug_disable_prompt (plug);
+ gs_lock_plug_set_busy (plug);
+ res = gs_auth_verify_user (g_get_user_name (), g_getenv ("DISPLAY"), auth_message_handler, plug, &error);
+
+ gs_debug ("Verify user returned: %s", res ? "TRUE" : "FALSE");
+ if (! res)
+ {
+ if (error != NULL)
+ {
+ gs_debug ("Verify user returned error: %s", error->message);
+ gs_lock_plug_show_message (plug, error->message);
+ }
+ else
+ {
+ gs_lock_plug_show_message (plug, _("Authentication failed."));
+ }
+
+ printf ("NOTICE=AUTH FAILED\n");
+ fflush (stdout);
+
+ if (error != NULL)
+ {
+ g_error_free (error);
+ }
+ }
+
+ return res;
+}
+
+static void
+response_cb (GSLockPlug *plug,
+ gint response_id)
+{
+ if ((response_id == GS_LOCK_PLUG_RESPONSE_CANCEL) ||
+ (response_id == GTK_RESPONSE_DELETE_EVENT))
+ {
+ quit_response_cancel ();
+ }
+}
+
+static gboolean
+response_request_quit (void)
+{
+ printf ("REQUEST QUIT\n");
+ fflush (stdout);
+ return FALSE;
+}
+
+static gboolean
+quit_timeout_cb (gpointer data)
+{
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static gboolean
+auth_check_idle (GSLockPlug *plug)
+{
+ gboolean res;
+ gboolean again;
+ static guint loop_counter = 0;
+
+ again = TRUE;
+ res = do_auth_check (plug);
+
+ if (res)
+ {
+ again = FALSE;
+ g_idle_add ((GSourceFunc)quit_response_ok, NULL);
+ }
+ else
+ {
+ loop_counter++;
+
+ if (loop_counter < MAX_FAILURES)
+ {
+ gs_debug ("Authentication failed, retrying (%u)", loop_counter);
+ g_timeout_add (3000, (GSourceFunc)reset_idle_cb, plug);
+ }
+ else
+ {
+ gs_debug ("Authentication failed, quitting (max failures)");
+ again = FALSE;
+ /* Don't quit immediately, but rather request that mate-screensaver
+ * terminates us after it has finished the dialog shake. Time out
+ * after 5 seconds and quit anyway if this doesn't happen though */
+ g_idle_add ((GSourceFunc)response_request_quit, NULL);
+ g_timeout_add (5000, (GSourceFunc)quit_timeout_cb, NULL);
+ }
+ }
+
+ return again;
+}
+
+static void
+show_cb (GtkWidget *widget,
+ gpointer data)
+{
+ print_id (widget);
+}
+
+static gboolean
+popup_dialog_idle (void)
+{
+ GtkWidget *widget;
+
+ gs_profile_start (NULL);
+
+ widget = gs_lock_plug_new ();
+
+ if (enable_logout)
+ {
+ g_object_set (widget, "logout-enabled", TRUE, NULL);
+ }
+
+ if (logout_command)
+ {
+ g_object_set (widget, "logout-command", logout_command, NULL);
+ }
+
+ if (enable_switch)
+ {
+ g_object_set (widget, "switch-enabled", TRUE, NULL);
+ }
+
+ if (status_message)
+ {
+ g_object_set (widget, "status-message", status_message, NULL);
+ }
+
+ g_signal_connect (GS_LOCK_PLUG (widget), "response", G_CALLBACK (response_cb), NULL);
+ g_signal_connect (widget, "show", G_CALLBACK (show_cb), NULL);
+
+ gtk_widget_realize (widget);
+
+ g_idle_add ((GSourceFunc)auth_check_idle, widget);
+
+ gs_profile_end (NULL);
+
+ return FALSE;
+}
+
+
+/*
+ * Copyright (c) 1991-2004 Jamie Zawinski <[email protected]>
+ * Copyright (c) 2005 William Jon McCann <[email protected]>
+ *
+ * Initializations that potentially take place as a priveleged user:
+ If the executable is setuid root, then these initializations
+ are run as root, before discarding privileges.
+*/
+static gboolean
+privileged_initialization (int *argc,
+ char **argv,
+ gboolean verbose)
+{
+ gboolean ret;
+ char *nolock_reason;
+ char *orig_uid;
+ char *uid_message;
+
+ gs_profile_start (NULL);
+
+#ifndef NO_LOCKING
+ /* before hack_uid () for proper permissions */
+ gs_auth_priv_init ();
+#endif /* NO_LOCKING */
+
+ ret = hack_uid (&nolock_reason,
+ &orig_uid,
+ &uid_message);
+
+ if (nolock_reason)
+ {
+ g_debug ("Locking disabled: %s", nolock_reason);
+ }
+
+ if (uid_message && verbose)
+ {
+ g_print ("Modified UID: %s", uid_message);
+ }
+
+ g_free (nolock_reason);
+ g_free (orig_uid);
+ g_free (uid_message);
+
+ gs_profile_end (NULL);
+
+ return ret;
+}
+
+
+/*
+ * Copyright (c) 1991-2004 Jamie Zawinski <[email protected]>
+ * Copyright (c) 2005 William Jon McCann <[email protected]>
+ *
+ * Figure out what locking mechanisms are supported.
+ */
+static gboolean
+lock_initialization (int *argc,
+ char **argv,
+ char **nolock_reason,
+ gboolean verbose)
+{
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = NULL;
+ }
+
+#ifdef NO_LOCKING
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("not compiled with locking support");
+ }
+
+ return FALSE;
+#else /* !NO_LOCKING */
+
+ /* Finish initializing locking, now that we're out of privileged code. */
+ if (! gs_auth_init ())
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("error getting password");
+ }
+
+ return FALSE;
+ }
+
+ /* If locking is currently enabled, but the environment indicates that
+ we have been launched as MDM's "Background" program, then disable
+ locking just in case.
+ */
+ if (getenv ("RUNNING_UNDER_MDM"))
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("running under MDM");
+ }
+
+ return FALSE;
+ }
+
+ /* If the server is XDarwin (MacOS X) then disable locking.
+ (X grabs only affect X programs, so you can use Command-Tab
+ to bring any other Mac program to the front, e.g., Terminal.)
+ */
+ {
+ gboolean macos = FALSE;
+
+#ifdef __APPLE__
+ /* Disable locking if *running* on Apple hardware, since we have no
+ reliable way to determine whether the server is running on MacOS.
+ Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
+ but I'm not really sure about that.
+ */
+ macos = TRUE;
+#endif
+
+ if (macos)
+ {
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup ("Cannot lock securely on MacOS X");
+ }
+
+ return FALSE;
+ }
+ }
+
+#endif /* NO_LOCKING */
+
+ return TRUE;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+ char *nolock_reason = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! g_thread_supported ())
+ {
+ g_thread_init (NULL);
+ }
+
+ g_type_init ();
+
+ gs_profile_start (NULL);
+
+ if (! privileged_initialization (&argc, argv, verbose))
+ {
+ response_lock_init_failed ();
+ exit (1);
+ }
+
+ error = NULL;
+ if (! gtk_init_with_args (&argc, &argv, NULL, entries, NULL, &error))
+ {
+ if (error != NULL)
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ }
+ exit (1);
+ }
+
+ if (show_version)
+ {
+ g_print ("%s %s\n", argv [0], VERSION);
+ exit (1);
+ }
+
+ if (! lock_initialization (&argc, argv, &nolock_reason, verbose))
+ {
+ if (nolock_reason != NULL)
+ {
+ g_debug ("Screen locking disabled: %s", nolock_reason);
+ g_free (nolock_reason);
+ }
+ response_lock_init_failed ();
+ exit (1);
+ }
+
+ gs_debug_init (verbose, FALSE);
+
+ g_idle_add ((GSourceFunc)popup_dialog_idle, NULL);
+
+ gtk_main ();
+
+ gs_profile_end (NULL);
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/mate-screensaver-gl-helper.c b/src/mate-screensaver-gl-helper.c
new file mode 100644
index 0000000..fd128f5
--- /dev/null
+++ b/src/mate-screensaver-gl-helper.c
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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 <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "gs-visual-gl.h"
+
+int
+main (int argc,
+ char **argv)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ GdkVisual *visual;
+ Visual *xvisual;
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ if (error != NULL)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("Unable to initialize GTK+");
+ }
+ exit (1);
+ }
+
+ display = gdk_display_get_default ();
+ screen = gdk_display_get_default_screen (display);
+ visual = gs_visual_gl_get_best_for_screen (screen);
+
+ if (visual != NULL)
+ {
+ xvisual = gdk_x11_visual_get_xvisual (visual);
+ printf ("0x%x\n", (unsigned int) XVisualIDFromVisual (xvisual));
+ }
+ else
+ {
+ printf ("none\n");
+ }
+
+ return 0;
+}
diff --git a/src/mate-screensaver-gl-helper.c.orig b/src/mate-screensaver-gl-helper.c.orig
new file mode 100644
index 0000000..70b56e4
--- /dev/null
+++ b/src/mate-screensaver-gl-helper.c.orig
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2006 William Jon McCann <[email protected]>
+ *
+ * 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 <glib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "gs-visual-gl.h"
+
+int
+main (int argc,
+ char **argv)
+{
+ GdkDisplay *display;
+ GdkScreen *screen;
+ GdkVisual *visual;
+ Visual *xvisual;
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ g_type_init ();
+
+ g_set_prgname (argv[0]);
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ if (error != NULL)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("Unable to initialize GTK+");
+ }
+ exit (1);
+ }
+
+ display = gdk_display_get_default ();
+ screen = gdk_display_get_default_screen (display);
+ visual = gs_visual_gl_get_best_for_screen (screen);
+
+ if (visual != NULL)
+ {
+ xvisual = gdk_x11_visual_get_xvisual (visual);
+ printf ("0x%x\n", (unsigned int) XVisualIDFromVisual (xvisual));
+ }
+ else
+ {
+ printf ("none\n");
+ }
+
+ return 0;
+}
diff --git a/src/mate-screensaver-preferences.c b/src/mate-screensaver-preferences.c
new file mode 100644
index 0000000..5bcf5e5
--- /dev/null
+++ b/src/mate-screensaver-preferences.c
@@ -0,0 +1,1781 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ * Rodrigo Moya <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h> /* For uid_t, gid_t */
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+#include <gio/gio.h>
+
+#include "copy-theme-dialog.h"
+
+#include "gs-theme-manager.h"
+#include "gs-job.h"
+#include "gs-prefs.h" /* for GS_MODE enum */
+
+#define GTK_BUILDER_FILE "mate-screensaver-preferences.ui"
+
+#define MATE_LOCKDOWN_DIR "/desktop/mate/lockdown"
+#define KEY_LOCK_DISABLE MATE_LOCKDOWN_DIR "/disable_lock_screen"
+
+#define KEY_DIR "/apps/mate-screensaver"
+#define MATE_SESSION_DIR "/desktop/mate/session"
+#define KEY_LOCK KEY_DIR "/lock_enabled"
+#define KEY_IDLE_ACTIVATION_ENABLED KEY_DIR "/idle_activation_enabled"
+#define KEY_MODE KEY_DIR "/mode"
+#define KEY_ACTIVATE_DELAY MATE_SESSION_DIR "/idle_delay"
+#define KEY_LOCK_DELAY KEY_DIR "/lock_delay"
+#define KEY_CYCLE_DELAY KEY_DIR "/cycle_delay"
+#define KEY_THEMES KEY_DIR "/themes"
+
+#define GPM_COMMAND "mate-power-preferences"
+
+enum
+{
+ NAME_COLUMN = 0,
+ ID_COLUMN,
+ N_COLUMNS
+};
+
+static MateConfEnumStringPair mode_enum_map [] =
+{
+ { GS_MODE_BLANK_ONLY, "blank-only" },
+ { GS_MODE_RANDOM, "random" },
+ { GS_MODE_SINGLE, "single" },
+ { 0, NULL }
+};
+
+/* Drag and drop info */
+enum
+{
+ TARGET_URI_LIST,
+ TARGET_NS_URL
+};
+
+static GtkTargetEntry drop_types [] =
+{
+ { "text/uri-list", 0, TARGET_URI_LIST },
+ { "_NETSCAPE_URL", 0, TARGET_NS_URL }
+};
+
+static GtkBuilder *builder = NULL;
+static GSThemeManager *theme_manager = NULL;
+static GSJob *job = NULL;
+
+static gint32
+config_get_activate_delay (gboolean *is_writable)
+{
+ MateConfClient *client;
+ gint32 delay;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_ACTIVATE_DELAY,
+ NULL);
+ }
+
+ delay = mateconf_client_get_int (client, KEY_ACTIVATE_DELAY, NULL);
+
+ if (delay < 1)
+ {
+ delay = 1;
+ }
+
+ g_object_unref (client);
+
+ return delay;
+}
+
+static void
+config_set_activate_delay (gint32 timeout)
+{
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_set_int (client, KEY_ACTIVATE_DELAY, timeout, NULL);
+
+ g_object_unref (client);
+}
+
+static int
+config_get_mode (gboolean *is_writable)
+{
+ MateConfClient *client;
+ int mode;
+ char *string;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_MODE,
+ NULL);
+ }
+
+ string = mateconf_client_get_string (client, KEY_MODE, NULL);
+ if (string)
+ {
+ mateconf_string_to_enum (mode_enum_map, string, &mode);
+ g_free (string);
+ }
+ else
+ {
+ mode = GS_MODE_BLANK_ONLY;
+ }
+
+ g_object_unref (client);
+
+ return mode;
+}
+
+static void
+config_set_mode (int mode)
+{
+ MateConfClient *client;
+ const char *mode_string;
+
+ client = mateconf_client_get_default ();
+
+ mode_string = mateconf_enum_to_string (mode_enum_map, mode);
+ mateconf_client_set_string (client, KEY_MODE, mode_string, NULL);
+
+ g_object_unref (client);
+}
+
+static char *
+config_get_theme (gboolean *is_writable)
+{
+ MateConfClient *client;
+ char *name;
+ int mode;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ gboolean can_write_theme;
+ gboolean can_write_mode;
+
+ can_write_theme = mateconf_client_key_is_writable (client,
+ KEY_THEMES,
+ NULL);
+ can_write_mode = mateconf_client_key_is_writable (client,
+ KEY_MODE,
+ NULL);
+ *is_writable = can_write_theme && can_write_mode;
+ }
+
+ mode = config_get_mode (NULL);
+
+ name = NULL;
+ if (mode == GS_MODE_BLANK_ONLY)
+ {
+ name = g_strdup ("__blank-only");
+ }
+ else if (mode == GS_MODE_RANDOM)
+ {
+ name = g_strdup ("__random");
+ }
+ else
+ {
+ GSList *list;
+ list = mateconf_client_get_list (client,
+ KEY_THEMES,
+ MATECONF_VALUE_STRING,
+ NULL);
+ if (list != NULL)
+ {
+ name = g_strdup (list->data);
+ }
+ else
+ {
+ /* TODO: handle error */
+ /* default to blank */
+ name = g_strdup ("__blank-only");
+ }
+
+ g_slist_foreach (list, (GFunc)g_free, NULL);
+ g_slist_free (list);
+ }
+
+ g_object_unref (client);
+
+ return name;
+}
+
+static GSList *
+get_all_theme_ids (GSThemeManager *theme_manager)
+{
+ GSList *ids = NULL;
+ GSList *entries;
+ GSList *l;
+
+ entries = gs_theme_manager_get_info_list (theme_manager);
+ for (l = entries; l; l = l->next)
+ {
+ GSThemeInfo *info = l->data;
+
+ ids = g_slist_prepend (ids, g_strdup (gs_theme_info_get_id (info)));
+ gs_theme_info_unref (info);
+ }
+ g_slist_free (entries);
+
+ return ids;
+}
+
+static void
+config_set_theme (const char *theme_id)
+{
+ MateConfClient *client;
+ GSList *list = NULL;
+ int mode;
+
+ client = mateconf_client_get_default ();
+
+ if (theme_id && strcmp (theme_id, "__blank-only") == 0)
+ {
+ mode = GS_MODE_BLANK_ONLY;
+ }
+ else if (theme_id && strcmp (theme_id, "__random") == 0)
+ {
+ mode = GS_MODE_RANDOM;
+
+ /* set the themes key to contain all available screensavers */
+ list = get_all_theme_ids (theme_manager);
+ }
+ else
+ {
+ mode = GS_MODE_SINGLE;
+ list = g_slist_append (list, g_strdup (theme_id));
+ }
+
+ config_set_mode (mode);
+
+ mateconf_client_set_list (client,
+ KEY_THEMES,
+ MATECONF_VALUE_STRING,
+ list,
+ NULL);
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+
+ g_object_unref (client);
+}
+
+static gboolean
+config_get_enabled (gboolean *is_writable)
+{
+ int enabled;
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_LOCK,
+ NULL);
+ }
+
+ enabled = mateconf_client_get_bool (client, KEY_IDLE_ACTIVATION_ENABLED, NULL);
+
+ g_object_unref (client);
+
+ return enabled;
+}
+
+static void
+config_set_enabled (gboolean enabled)
+{
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_set_bool (client, KEY_IDLE_ACTIVATION_ENABLED, enabled, NULL);
+
+ g_object_unref (client);
+}
+
+static gboolean
+config_get_lock (gboolean *is_writable)
+{
+ MateConfClient *client;
+ gboolean lock;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_LOCK,
+ NULL);
+ }
+
+ lock = mateconf_client_get_bool (client, KEY_LOCK, NULL);
+
+ g_object_unref (client);
+
+ return lock;
+}
+
+static gboolean
+config_get_lock_disabled ()
+{
+ MateConfClient *client;
+ gboolean lock;
+
+ client = mateconf_client_get_default ();
+
+ lock = mateconf_client_get_bool (client, KEY_LOCK_DISABLE, NULL);
+
+ g_object_unref (client);
+ return lock;
+}
+
+static void
+config_set_lock (gboolean lock)
+{
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_set_bool (client, KEY_LOCK, lock, NULL);
+
+ g_object_unref (client);
+}
+
+static void
+preview_clear (GtkWidget *widget)
+{
+ GdkColor color = { 0, 0, 0 };
+
+ gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, &color);
+ gdk_window_clear (widget->window);
+}
+
+static void
+job_set_theme (GSJob *job,
+ const char *theme)
+{
+ GSThemeInfo *info;
+ const char *command;
+
+ command = NULL;
+
+ info = gs_theme_manager_lookup_theme_info (theme_manager, theme);
+ if (info != NULL)
+ {
+ command = gs_theme_info_get_exec (info);
+ }
+
+ gs_job_set_command (job, command);
+
+ if (info != NULL)
+ {
+ gs_theme_info_unref (info);
+ }
+}
+
+static void
+preview_set_theme (GtkWidget *widget,
+ const char *theme,
+ const char *name)
+{
+ GtkWidget *label;
+ char *markup;
+
+ if (job != NULL)
+ {
+ gs_job_stop (job);
+ }
+
+ preview_clear (widget);
+
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_theme_label"));
+ markup = g_markup_printf_escaped ("<i>%s</i>", name);
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+
+ if ((theme && strcmp (theme, "__blank-only") == 0))
+ {
+
+ }
+ else if (theme && strcmp (theme, "__random") == 0)
+ {
+ GSList *themes;
+
+ themes = get_all_theme_ids (theme_manager);
+ if (themes != NULL)
+ {
+ GSList *l;
+ gint32 i;
+
+ i = g_random_int_range (0, g_slist_length (themes));
+ l = g_slist_nth (themes, i);
+
+ job_set_theme (job, (const char *) l->data);
+ g_slist_foreach (themes, (GFunc) g_free, NULL);
+ g_slist_free (themes);
+
+ gs_job_start (job);
+ }
+ }
+ else
+ {
+ job_set_theme (job, theme);
+ gs_job_start (job);
+ }
+}
+
+static void
+help_display (void)
+{
+ GError *error = NULL;
+ char *command;
+ const char *lang;
+ char *uri = NULL;
+ GdkScreen *gscreen;
+ int i;
+
+ const char * const * langs = g_get_language_names ();
+
+ for (i = 0; langs[i] != NULL; i++)
+ {
+ lang = langs[i];
+ if (strchr (lang, '.'))
+ {
+ continue;
+ }
+
+ uri = g_build_filename (DATADIR,
+ "/mate/help/user-guide/",
+ lang,
+ "/user-guide.xml",
+ NULL);
+
+ if (g_file_test (uri, G_FILE_TEST_EXISTS))
+ {
+ break;
+ }
+ }
+
+ command = g_strconcat ("mate-open ghelp://",
+ uri,
+ "?prefs-screensaver",
+ NULL);
+ gscreen = gdk_screen_get_default ();
+ gdk_spawn_command_line_on_screen (gscreen, command, &error);
+
+ if (error != NULL)
+ {
+ GtkWidget *d;
+
+ d = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "%s", error->message);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (d);
+ g_error_free (error);
+ }
+
+ g_free (command);
+ g_free (uri);
+}
+
+static void
+response_cb (GtkWidget *widget,
+ int response_id)
+{
+
+ if (response_id == GTK_RESPONSE_HELP)
+ {
+ help_display ();
+ }
+ else if (response_id == GTK_RESPONSE_REJECT)
+ {
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+
+ res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (),
+ GPM_COMMAND,
+ &error);
+ if (! res)
+ {
+ g_warning ("Unable to start power management preferences: %s", error->message);
+ g_error_free (error);
+ }
+ }
+ else
+ {
+ gtk_widget_destroy (widget);
+ gtk_main_quit ();
+ }
+}
+
+static GSList *
+get_theme_info_list (void)
+{
+ return gs_theme_manager_get_info_list (theme_manager);
+}
+
+static void
+populate_model (GtkTreeStore *store)
+{
+ GtkTreeIter iter;
+ GSList *themes = NULL;
+ GSList *l;
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, _("Blank screen"),
+ ID_COLUMN, "__blank-only",
+ -1);
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, _("Random"),
+ ID_COLUMN, "__random",
+ -1);
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, NULL,
+ ID_COLUMN, "__separator",
+ -1);
+
+ themes = get_theme_info_list ();
+
+ if (themes == NULL)
+ {
+ return;
+ }
+
+ for (l = themes; l; l = l->next)
+ {
+ const char *name;
+ const char *id;
+ GSThemeInfo *info = l->data;
+
+ if (info == NULL)
+ {
+ continue;
+ }
+
+ name = gs_theme_info_get_name (info);
+ id = gs_theme_info_get_id (info);
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, name,
+ ID_COLUMN, id,
+ -1);
+
+ gs_theme_info_unref (info);
+ }
+
+ g_slist_free (themes);
+}
+
+static void
+tree_selection_previous (GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ if (! gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ return;
+ }
+
+ path = gtk_tree_model_get_path (model, &iter);
+ if (gtk_tree_path_prev (path))
+ {
+ gtk_tree_selection_select_path (selection, path);
+ }
+}
+
+static void
+tree_selection_next (GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ if (! gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ return;
+ }
+
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_path_next (path);
+ gtk_tree_selection_select_path (selection, path);
+}
+
+static void
+tree_selection_changed_cb (GtkTreeSelection *selection,
+ GtkWidget *preview)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ char *theme;
+ char *name;
+
+ if (! gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ return;
+ }
+
+ gtk_tree_model_get (model, &iter, ID_COLUMN, &theme, NAME_COLUMN, &name, -1);
+
+ if (theme == NULL)
+ {
+ g_free (name);
+ return;
+ }
+
+ preview_set_theme (preview, theme, name);
+ config_set_theme (theme);
+
+ g_free (theme);
+ g_free (name);
+}
+
+static void
+activate_delay_value_changed_cb (GtkRange *range,
+ gpointer user_data)
+{
+ gdouble value;
+
+ value = gtk_range_get_value (range);
+ config_set_activate_delay ((gint32)value);
+}
+
+static int
+compare_theme_names (char *name_a,
+ char *name_b,
+ char *id_a,
+ char *id_b)
+{
+
+ if (id_a == NULL)
+ {
+ return 1;
+ }
+ else if (id_b == NULL)
+ {
+ return -1;
+ }
+
+ if (strcmp (id_a, "__blank-only") == 0)
+ {
+ return -1;
+ }
+ else if (strcmp (id_b, "__blank-only") == 0)
+ {
+ return 1;
+ }
+ else if (strcmp (id_a, "__random") == 0)
+ {
+ return -1;
+ }
+ else if (strcmp (id_b, "__random") == 0)
+ {
+ return 1;
+ }
+ else if (strcmp (id_a, "__separator") == 0)
+ {
+ return -1;
+ }
+ else if (strcmp (id_b, "__separator") == 0)
+ {
+ return 1;
+ }
+
+ if (name_a == NULL)
+ {
+ return 1;
+ }
+ else if (name_b == NULL)
+ {
+ return -1;
+ }
+
+ return g_utf8_collate (name_a, name_b);
+}
+
+static int
+compare_theme (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ char *name_a;
+ char *name_b;
+ char *id_a;
+ char *id_b;
+ int result;
+
+ gtk_tree_model_get (model, a, NAME_COLUMN, &name_a, -1);
+ gtk_tree_model_get (model, b, NAME_COLUMN, &name_b, -1);
+ gtk_tree_model_get (model, a, ID_COLUMN, &id_a, -1);
+ gtk_tree_model_get (model, b, ID_COLUMN, &id_b, -1);
+
+ result = compare_theme_names (name_a, name_b, id_a, id_b);
+
+ g_free (name_a);
+ g_free (name_b);
+ g_free (id_a);
+ g_free (id_b);
+
+ return result;
+}
+
+static gboolean
+separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ int column = GPOINTER_TO_INT (data);
+ char *text;
+
+ gtk_tree_model_get (model, iter, column, &text, -1);
+
+ if (text != NULL && strcmp (text, "__separator") == 0)
+ {
+ return TRUE;
+ }
+
+ g_free (text);
+
+ return FALSE;
+}
+
+static void
+setup_treeview (GtkWidget *tree,
+ GtkWidget *preview)
+{
+ GtkTreeStore *store;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *select;
+
+ store = gtk_tree_store_new (N_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ populate_model (store);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (tree),
+ GTK_TREE_MODEL (store));
+
+ g_object_unref (store);
+
+#if GTK_CHECK_VERSION(2,10,0)
+ g_object_set (tree, "show-expanders", FALSE, NULL);
+#endif
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
+ "text", NAME_COLUMN,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
+
+ gtk_tree_view_column_set_sort_column_id (column, NAME_COLUMN);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
+ NAME_COLUMN,
+ compare_theme,
+ NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ NAME_COLUMN,
+ GTK_SORT_ASCENDING);
+
+ gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (tree),
+ separator_func,
+ GINT_TO_POINTER (ID_COLUMN),
+ NULL);
+
+ select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
+ gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
+ g_signal_connect (G_OBJECT (select), "changed",
+ G_CALLBACK (tree_selection_changed_cb),
+ preview);
+
+}
+
+static void
+setup_treeview_selection (GtkWidget *tree)
+{
+ char *theme;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path = NULL;
+ gboolean is_writable;
+
+ theme = config_get_theme (&is_writable);
+
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (tree, FALSE);
+ }
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+
+ if (theme && gtk_tree_model_get_iter_first (model, &iter))
+ {
+
+ do
+ {
+ char *id;
+ gboolean found;
+
+ gtk_tree_model_get (model, &iter,
+ ID_COLUMN, &id, -1);
+ found = (id && strcmp (id, theme) == 0);
+ g_free (id);
+
+ if (found)
+ {
+ path = gtk_tree_model_get_path (model, &iter);
+ break;
+ }
+
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ if (path)
+ {
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree),
+ path,
+ NULL,
+ FALSE);
+
+ gtk_tree_path_free (path);
+ }
+
+ g_free (theme);
+}
+
+static void
+reload_themes (void)
+{
+ GtkWidget *treeview;
+ GtkTreeModel *model;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+ gtk_tree_store_clear (GTK_TREE_STORE (model));
+ populate_model (GTK_TREE_STORE (model));
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
+ GTK_TREE_MODEL (model));
+}
+
+static void
+theme_copy_complete_cb (GtkWidget *dialog, gpointer user_data)
+{
+ reload_themes ();
+ gtk_widget_destroy (dialog);
+}
+
+static void
+theme_installer_run (GtkWidget *prefs_dialog, GList *files)
+{
+ GtkWidget *copy_dialog;
+
+ copy_dialog = copy_theme_dialog_new (files);
+ g_list_foreach (files, (GFunc) (g_object_unref), NULL);
+ g_list_free (files);
+
+ gtk_window_set_transient_for (GTK_WINDOW (copy_dialog),
+ GTK_WINDOW (prefs_dialog));
+ gtk_window_set_icon_name (GTK_WINDOW (copy_dialog),
+ "preferences-desktop-screensaver");
+
+ g_signal_connect (copy_dialog, "complete",
+ G_CALLBACK (theme_copy_complete_cb), NULL);
+
+ copy_theme_dialog_begin (COPY_THEME_DIALOG (copy_dialog));
+}
+
+/* Callback issued during drag movements */
+static gboolean
+drag_motion_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time,
+ gpointer data)
+{
+ return FALSE;
+}
+
+/* Callback issued during drag leaves */
+static void
+drag_leave_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ gpointer data)
+{
+ gtk_widget_queue_draw (widget);
+}
+
+/* GIO has no version of mate_vfs_uri_list_parse(), so copy from MateVFS
+ * and re-work to create GFiles.
+**/
+static GList *
+uri_list_parse (const gchar *uri_list)
+{
+ const gchar *p, *q;
+ gchar *retval;
+ GFile *file;
+ GList *result = NULL;
+
+ g_return_val_if_fail (uri_list != NULL, NULL);
+
+ p = uri_list;
+
+ /* We don't actually try to validate the URI according to RFC
+ * 2396, or even check for allowed characters - we just ignore
+ * comments and trim whitespace off the ends. We also
+ * allow LF delimination as well as the specified CRLF.
+ */
+ while (p != NULL)
+ {
+ if (*p != '#')
+ {
+ while (g_ascii_isspace (*p))
+ p++;
+
+ q = p;
+ while ((*q != '\0')
+ && (*q != '\n')
+ && (*q != '\r'))
+ q++;
+
+ if (q > p)
+ {
+ q--;
+ while (q > p
+ && g_ascii_isspace (*q))
+ q--;
+
+ retval = g_malloc (q - p + 2);
+ strncpy (retval, p, q - p + 1);
+ retval[q - p + 1] = '\0';
+
+ file = g_file_new_for_uri (retval);
+
+ g_free (retval);
+
+ if (file != NULL)
+ result = g_list_prepend (result, file);
+ }
+ }
+ p = strchr (p, '\n');
+ if (p != NULL)
+ p++;
+ }
+
+ return g_list_reverse (result);
+}
+
+/* Callback issued on actual drops. Attempts to load the file dropped. */
+static void
+drag_data_received_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ GList *files;
+
+ if (!(info == TARGET_URI_LIST || info == TARGET_NS_URL))
+ return;
+
+ files = uri_list_parse ((char *) selection_data->data);
+ if (files != NULL)
+ {
+ GtkWidget *prefs_dialog;
+
+ prefs_dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ theme_installer_run (prefs_dialog, files);
+ }
+}
+
+/* Adapted from totem_time_to_string_text */
+static char *
+time_to_string_text (long time)
+{
+ char *secs, *mins, *hours, *string;
+ int sec, min, hour;
+
+ sec = time % 60;
+ time = time - sec;
+ min = (time % (60 * 60)) / 60;
+ time = time - (min * 60);
+ hour = time / (60 * 60);
+
+ hours = g_strdup_printf (ngettext ("%d hour",
+ "%d hours", hour), hour);
+
+ mins = g_strdup_printf (ngettext ("%d minute",
+ "%d minutes", min), min);
+
+ secs = g_strdup_printf (ngettext ("%d second",
+ "%d seconds", sec), sec);
+
+ if (hour > 0)
+ {
+ if (sec > 0)
+ {
+ /* hour:minutes:seconds */
+ string = g_strdup_printf (_("%s %s %s"), hours, mins, secs);
+ }
+ else if (min > 0)
+ {
+ /* hour:minutes */
+ string = g_strdup_printf (_("%s %s"), hours, mins);
+ }
+ else
+ {
+ /* hour */
+ string = g_strdup_printf (_("%s"), hours);
+ }
+ }
+ else if (min > 0)
+ {
+ if (sec > 0)
+ {
+ /* minutes:seconds */
+ string = g_strdup_printf (_("%s %s"), mins, secs);
+ }
+ else
+ {
+ /* minutes */
+ string = g_strdup_printf (_("%s"), mins);
+ }
+ }
+ else
+ {
+ /* seconds */
+ string = g_strdup_printf (_("%s"), secs);
+ }
+
+ g_free (hours);
+ g_free (mins);
+ g_free (secs);
+
+ return string;
+}
+
+static char *
+format_value_callback_time (GtkScale *scale,
+ gdouble value)
+{
+ if (value == 0)
+ return g_strdup_printf (_("Never"));
+
+ return time_to_string_text (value * 60.0);
+}
+
+static void
+lock_checkbox_toggled (GtkToggleButton *button, gpointer user_data)
+{
+ config_set_lock (gtk_toggle_button_get_active (button));
+}
+
+static void
+enabled_checkbox_toggled (GtkToggleButton *button, gpointer user_data)
+{
+ config_set_enabled (gtk_toggle_button_get_active (button));
+}
+
+static void
+invalid_type_warning (const char *type)
+{
+ g_warning ("Error retrieving configuration key '%s': Invalid type",
+ type);
+}
+
+static void
+ui_disable_lock (gboolean disable)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ gtk_widget_set_sensitive (widget, !disable);
+ if (disable)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+ }
+}
+
+static void
+ui_set_lock (gboolean enabled)
+{
+ GtkWidget *widget;
+ gboolean active;
+ gboolean lock_disabled;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ if (active != enabled)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), enabled);
+ }
+ lock_disabled = config_get_lock_disabled ();
+ ui_disable_lock (lock_disabled);
+}
+
+static void
+ui_set_enabled (gboolean enabled)
+{
+ GtkWidget *widget;
+ gboolean active;
+ gboolean is_writable;
+ gboolean lock_disabled;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "enable_checkbox"));
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ if (active != enabled)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), enabled);
+ }
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ config_get_lock (&is_writable);
+ if (is_writable)
+ {
+ gtk_widget_set_sensitive (widget, enabled);
+ }
+ lock_disabled = config_get_lock_disabled ();
+ ui_disable_lock(lock_disabled);
+}
+
+static void
+ui_set_delay (int delay)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_hscale"));
+ gtk_range_set_value (GTK_RANGE (widget), delay);
+}
+
+static void
+key_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer data)
+{
+ const char *key;
+ MateConfValue *value;
+
+ key = mateconf_entry_get_key (entry);
+
+ if (! g_str_has_prefix (key, KEY_DIR) && ! g_str_has_prefix (key, MATE_LOCKDOWN_DIR))
+ {
+ return;
+ }
+
+ value = mateconf_entry_get_value (entry);
+
+ if (strcmp (key, KEY_IDLE_ACTIVATION_ENABLED) == 0)
+ {
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+
+ ui_set_enabled (enabled);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_LOCK) == 0)
+ {
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+
+ ui_set_lock (enabled);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_LOCK_DISABLE) == 0)
+ {
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+
+ ui_disable_lock (disabled);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_THEMES) == 0)
+ {
+ if (value->type == MATECONF_VALUE_LIST)
+ {
+ GtkWidget *treeview;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ setup_treeview_selection (treeview);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_ACTIVATE_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ ui_set_delay (delay);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else
+ {
+ /*g_warning ("Config key not handled: %s", key);*/
+ }
+}
+
+static void
+fullscreen_preview_previous_cb (GtkWidget *fullscreen_preview_window,
+ gpointer user_data)
+{
+ GtkWidget *treeview;
+ GtkTreeSelection *selection;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+ tree_selection_previous (selection);
+}
+
+static void
+fullscreen_preview_next_cb (GtkWidget *fullscreen_preview_window,
+ gpointer user_data)
+{
+ GtkWidget *treeview;
+ GtkTreeSelection *selection;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+ tree_selection_next (selection);
+}
+
+static void
+fullscreen_preview_cancelled_cb (GtkWidget *button,
+ gpointer user_data)
+{
+
+ GtkWidget *fullscreen_preview_area;
+ GtkWidget *fullscreen_preview_window;
+ GtkWidget *preview_area;
+ GtkWidget *dialog;
+
+ preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "preview_area"));
+ gs_job_set_widget (job, preview_area);
+
+ fullscreen_preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_area"));
+ preview_clear (fullscreen_preview_area);
+
+ fullscreen_preview_window = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_window"));
+ gtk_widget_hide (fullscreen_preview_window);
+
+ dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ gtk_widget_show (dialog);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+fullscreen_preview_start_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget *fullscreen_preview_area;
+ GtkWidget *fullscreen_preview_window;
+ GtkWidget *dialog;
+
+ dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ gtk_widget_hide (dialog);
+
+ fullscreen_preview_window = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_window"));
+
+ gtk_window_fullscreen (GTK_WINDOW (fullscreen_preview_window));
+ gtk_window_set_keep_above (GTK_WINDOW (fullscreen_preview_window), TRUE);
+
+ gtk_widget_show (fullscreen_preview_window);
+ gtk_widget_grab_focus (fullscreen_preview_window);
+
+ fullscreen_preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_area"));
+ preview_clear (fullscreen_preview_area);
+ gs_job_set_widget (job, fullscreen_preview_area);
+}
+
+static void
+constrain_list_size (GtkWidget *widget,
+ GtkRequisition *requisition,
+ GtkWidget *to_size)
+{
+ GtkRequisition req;
+ int max_height;
+
+ /* constrain height to be the tree height up to a max */
+ max_height = (gdk_screen_get_height (gtk_widget_get_screen (widget))) / 4;
+
+ gtk_widget_size_request (to_size, &req);
+
+ requisition->height = MIN (req.height, max_height);
+}
+
+static void
+setup_list_size_constraint (GtkWidget *widget,
+ GtkWidget *to_size)
+{
+ g_signal_connect (widget, "size-request",
+ G_CALLBACK (constrain_list_size), to_size);
+}
+
+static gboolean
+check_is_root_user (void)
+{
+#ifndef G_OS_WIN32
+ uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
+ gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
+
+#ifdef HAVE_GETRESUID
+ if (getresuid (&ruid, &euid, &suid) != 0 ||
+ getresgid (&rgid, &egid, &sgid) != 0)
+#endif /* HAVE_GETRESUID */
+ {
+ suid = ruid = getuid ();
+ sgid = rgid = getgid ();
+ euid = geteuid ();
+ egid = getegid ();
+ }
+
+ if (ruid == 0)
+ {
+ return TRUE;
+ }
+
+#endif
+ return FALSE;
+}
+
+static void
+setup_for_root_user (void)
+{
+ GtkWidget *lock_checkbox;
+ GtkWidget *label;
+
+ lock_checkbox = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "root_warning_label"));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lock_checkbox), FALSE);
+ gtk_widget_set_sensitive (lock_checkbox, FALSE);
+
+ gtk_widget_show (label);
+}
+
+static GdkVisual *
+get_best_visual (void)
+{
+ char *command;
+ char *std_output;
+ int exit_status;
+ GError *error;
+ unsigned long v;
+ char c;
+ GdkVisual *visual;
+ gboolean res;
+
+ visual = NULL;
+
+ command = g_build_filename (LIBEXECDIR, "mate-screensaver-gl-helper", NULL);
+
+ error = NULL;
+ res = g_spawn_command_line_sync (command,
+ &std_output,
+ NULL,
+ &exit_status,
+ &error);
+
+ if (! res)
+ {
+ g_debug ("Could not run command '%s': %s", command, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (1 == sscanf (std_output, "0x%lx %c", &v, &c))
+ {
+ if (v != 0)
+ {
+ VisualID visual_id;
+
+ visual_id = (VisualID) v;
+ visual = gdkx_visual_get (visual_id);
+
+ g_debug ("Found best visual for GL: 0x%x",
+ (unsigned int) visual_id);
+ }
+ }
+
+out:
+ g_free (std_output);
+ g_free (command);
+
+ return visual;
+}
+
+static GdkColormap *
+get_best_colormap_for_screen (GdkScreen *screen)
+{
+ GdkColormap *colormap;
+ GdkVisual *visual;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ visual = get_best_visual ();
+
+ colormap = NULL;
+ if (visual != NULL)
+ {
+ colormap = gdk_colormap_new (visual, FALSE);
+ }
+
+ return colormap;
+}
+
+static void
+widget_set_best_colormap (GtkWidget *widget)
+{
+ GdkColormap *colormap;
+
+ g_return_if_fail (widget != NULL);
+
+ colormap = get_best_colormap_for_screen (gtk_widget_get_screen (widget));
+ if (colormap != NULL)
+ {
+ gtk_widget_set_colormap (widget, colormap);
+ g_object_unref (colormap);
+ }
+}
+
+static gboolean
+setup_treeview_idle (gpointer data)
+{
+ GtkWidget *preview;
+ GtkWidget *treeview;
+
+ preview = GTK_WIDGET (gtk_builder_get_object (builder, "preview_area"));
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+
+ setup_treeview (treeview, preview);
+ setup_treeview_selection (treeview);
+
+ return FALSE;
+}
+
+static gboolean
+is_program_in_path (const char *program)
+{
+ char *tmp = g_find_program_in_path (program);
+ if (tmp != NULL)
+ {
+ g_free (tmp);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static void
+init_capplet (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *preview;
+ GtkWidget *treeview;
+ GtkWidget *list_scroller;
+ GtkWidget *activate_delay_hscale;
+ GtkWidget *activate_delay_hbox;
+ GtkWidget *label;
+ GtkWidget *enabled_checkbox;
+ GtkWidget *lock_checkbox;
+ GtkWidget *root_warning_label;
+ GtkWidget *preview_button;
+ GtkWidget *gpm_button;
+ GtkWidget *fullscreen_preview_window;
+ GtkWidget *fullscreen_preview_previous;
+ GtkWidget *fullscreen_preview_next;
+ GtkWidget *fullscreen_preview_area;
+ GtkWidget *fullscreen_preview_close;
+ char *gtk_builder_file;
+ char *string;
+ gdouble activate_delay;
+ gboolean enabled;
+ gboolean is_writable;
+ MateConfClient *client;
+ GError *error=NULL;
+
+ gtk_builder_file = g_build_filename (GTKBUILDERDIR, GTK_BUILDER_FILE, NULL);
+ builder = gtk_builder_new();
+ if (!gtk_builder_add_from_file(builder, gtk_builder_file, &error))
+ {
+ g_warning("Couldn't load builder file: %s", error->message);
+ g_error_free(error);
+ }
+ g_free (gtk_builder_file);
+
+ if (builder == NULL)
+ {
+
+ dialog = gtk_message_dialog_new (NULL,
+ 0, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Could not load the main interface"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("Please make sure that the screensaver is properly installed"));
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ exit (1);
+ }
+
+ preview = GTK_WIDGET (gtk_builder_get_object (builder, "preview_area"));
+ dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ list_scroller = GTK_WIDGET (gtk_builder_get_object (builder, "themes_scrolled_window"));
+ activate_delay_hscale = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_hscale"));
+ activate_delay_hbox = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_hbox"));
+ enabled_checkbox = GTK_WIDGET (gtk_builder_get_object (builder, "enable_checkbox"));
+ lock_checkbox = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ root_warning_label = GTK_WIDGET (gtk_builder_get_object (builder, "root_warning_label"));
+ preview_button = GTK_WIDGET (gtk_builder_get_object (builder, "preview_button"));
+ gpm_button = GTK_WIDGET (gtk_builder_get_object (builder, "gpm_button"));
+ fullscreen_preview_window = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_window"));
+ fullscreen_preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_area"));
+ fullscreen_preview_close = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_close"));
+ fullscreen_preview_previous = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_previous_button"));
+ fullscreen_preview_next = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_next_button"));
+
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_label"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), activate_delay_hscale);
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "savers_label"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), treeview);
+
+ gtk_widget_set_no_show_all (root_warning_label, TRUE);
+ widget_set_best_colormap (preview);
+
+ if (! is_program_in_path (GPM_COMMAND))
+ {
+ gtk_widget_set_no_show_all (gpm_button, TRUE);
+ gtk_widget_hide (gpm_button);
+ }
+
+ activate_delay = config_get_activate_delay (&is_writable);
+ ui_set_delay (activate_delay);
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (activate_delay_hbox, FALSE);
+ }
+ g_signal_connect (activate_delay_hscale, "format-value",
+ G_CALLBACK (format_value_callback_time), NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lock_checkbox), config_get_lock (&is_writable));
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (lock_checkbox, FALSE);
+ }
+ g_signal_connect (lock_checkbox, "toggled",
+ G_CALLBACK (lock_checkbox_toggled), NULL);
+
+ enabled = config_get_enabled (&is_writable);
+ ui_set_enabled (enabled);
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (enabled_checkbox, FALSE);
+ }
+ g_signal_connect (enabled_checkbox, "toggled",
+ G_CALLBACK (enabled_checkbox_toggled), NULL);
+
+ setup_list_size_constraint (list_scroller, treeview);
+ gtk_widget_set_size_request (preview, 480, 300);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "preferences-desktop-screensaver");
+ gtk_window_set_icon_name (GTK_WINDOW (fullscreen_preview_window), "screensaver");
+
+ gtk_drag_dest_set (dialog, GTK_DEST_DEFAULT_ALL,
+ drop_types, G_N_ELEMENTS (drop_types),
+ GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
+
+ g_signal_connect (dialog, "drag-motion",
+ G_CALLBACK (drag_motion_cb), NULL);
+ g_signal_connect (dialog, "drag-leave",
+ G_CALLBACK (drag_leave_cb), NULL);
+ g_signal_connect (dialog, "drag-data-received",
+ G_CALLBACK (drag_data_received_cb), NULL);
+
+ gtk_widget_show_all (dialog);
+
+ /* Update list of themes if using random screensaver */
+ client = mateconf_client_get_default ();
+ string = mateconf_client_get_string (client, KEY_MODE, NULL);
+ if (string != NULL)
+ {
+ int mode;
+ GSList *list;
+
+ mateconf_string_to_enum (mode_enum_map, string, &mode);
+ g_free (string);
+
+ if (mode == GS_MODE_RANDOM)
+ {
+ list = get_all_theme_ids (theme_manager);
+ mateconf_client_set_list (client, KEY_THEMES, MATECONF_VALUE_STRING, list, NULL);
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+ }
+ }
+
+ mateconf_client_add_dir (client, KEY_DIR,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_notify_add (client,
+ KEY_DIR,
+ key_changed_cb,
+ NULL, NULL, NULL);
+ mateconf_client_add_dir (client, MATE_LOCKDOWN_DIR,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_notify_add (client,
+ MATE_LOCKDOWN_DIR,
+ key_changed_cb,
+ NULL, NULL, NULL);
+
+ g_object_unref (client);
+
+ preview_clear (preview);
+ gs_job_set_widget (job, preview);
+
+ if (check_is_root_user ())
+ {
+ setup_for_root_user ();
+ }
+
+ g_signal_connect (activate_delay_hscale, "value-changed",
+ G_CALLBACK (activate_delay_value_changed_cb), NULL);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (response_cb), NULL);
+
+ g_signal_connect (preview_button, "clicked",
+ G_CALLBACK (fullscreen_preview_start_cb),
+ treeview);
+
+ g_signal_connect (fullscreen_preview_close, "clicked",
+ G_CALLBACK (fullscreen_preview_cancelled_cb), NULL);
+ g_signal_connect (fullscreen_preview_previous, "clicked",
+ G_CALLBACK (fullscreen_preview_previous_cb), NULL);
+ g_signal_connect (fullscreen_preview_next, "clicked",
+ G_CALLBACK (fullscreen_preview_next_cb), NULL);
+
+ g_idle_add ((GSourceFunc)setup_treeview_idle, NULL);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ gtk_init (&argc, &argv);
+
+ job = gs_job_new ();
+ theme_manager = gs_theme_manager_new ();
+
+ init_capplet ();
+
+ gtk_main ();
+
+ g_object_unref (theme_manager);
+ g_object_unref (job);
+
+ return 0;
+}
diff --git a/src/mate-screensaver-preferences.c.orig b/src/mate-screensaver-preferences.c.orig
new file mode 100644
index 0000000..cedce78
--- /dev/null
+++ b/src/mate-screensaver-preferences.c.orig
@@ -0,0 +1,1781 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ * Rodrigo Moya <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h> /* For uid_t, gid_t */
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <mateconf/mateconf-client.h>
+
+#include <gio/gio.h>
+
+#include "copy-theme-dialog.h"
+
+#include "gs-theme-manager.h"
+#include "gs-job.h"
+#include "gs-prefs.h" /* for GS_MODE enum */
+
+#define GTK_BUILDER_FILE "mate-screensaver-preferences.ui"
+
+#define MATE_LOCKDOWN_DIR "/desktop/mate/lockdown"
+#define KEY_LOCK_DISABLE MATE_LOCKDOWN_DIR "/disable_lock_screen"
+
+#define KEY_DIR "/apps/mate-screensaver"
+#define MATE_SESSION_DIR "/desktop/mate/session"
+#define KEY_LOCK KEY_DIR "/lock_enabled"
+#define KEY_IDLE_ACTIVATION_ENABLED KEY_DIR "/idle_activation_enabled"
+#define KEY_MODE KEY_DIR "/mode"
+#define KEY_ACTIVATE_DELAY MATE_SESSION_DIR "/idle_delay"
+#define KEY_LOCK_DELAY KEY_DIR "/lock_delay"
+#define KEY_CYCLE_DELAY KEY_DIR "/cycle_delay"
+#define KEY_THEMES KEY_DIR "/themes"
+
+#define GPM_COMMAND "mate-power-preferences"
+
+enum
+{
+ NAME_COLUMN = 0,
+ ID_COLUMN,
+ N_COLUMNS
+};
+
+static MateConfEnumStringPair mode_enum_map [] =
+{
+ { GS_MODE_BLANK_ONLY, "blank-only" },
+ { GS_MODE_RANDOM, "random" },
+ { GS_MODE_SINGLE, "single" },
+ { 0, NULL }
+};
+
+/* Drag and drop info */
+enum
+{
+ TARGET_URI_LIST,
+ TARGET_NS_URL
+};
+
+static GtkTargetEntry drop_types [] =
+{
+ { "text/uri-list", 0, TARGET_URI_LIST },
+ { "_NETSCAPE_URL", 0, TARGET_NS_URL }
+};
+
+static GtkBuilder *builder = NULL;
+static GSThemeManager *theme_manager = NULL;
+static GSJob *job = NULL;
+
+static gint32
+config_get_activate_delay (gboolean *is_writable)
+{
+ MateConfClient *client;
+ gint32 delay;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_ACTIVATE_DELAY,
+ NULL);
+ }
+
+ delay = mateconf_client_get_int (client, KEY_ACTIVATE_DELAY, NULL);
+
+ if (delay < 1)
+ {
+ delay = 1;
+ }
+
+ g_object_unref (client);
+
+ return delay;
+}
+
+static void
+config_set_activate_delay (gint32 timeout)
+{
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_set_int (client, KEY_ACTIVATE_DELAY, timeout, NULL);
+
+ g_object_unref (client);
+}
+
+static int
+config_get_mode (gboolean *is_writable)
+{
+ MateConfClient *client;
+ int mode;
+ char *string;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_MODE,
+ NULL);
+ }
+
+ string = mateconf_client_get_string (client, KEY_MODE, NULL);
+ if (string)
+ {
+ mateconf_string_to_enum (mode_enum_map, string, &mode);
+ g_free (string);
+ }
+ else
+ {
+ mode = GS_MODE_BLANK_ONLY;
+ }
+
+ g_object_unref (client);
+
+ return mode;
+}
+
+static void
+config_set_mode (int mode)
+{
+ MateConfClient *client;
+ const char *mode_string;
+
+ client = mateconf_client_get_default ();
+
+ mode_string = mateconf_enum_to_string (mode_enum_map, mode);
+ mateconf_client_set_string (client, KEY_MODE, mode_string, NULL);
+
+ g_object_unref (client);
+}
+
+static char *
+config_get_theme (gboolean *is_writable)
+{
+ MateConfClient *client;
+ char *name;
+ int mode;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ gboolean can_write_theme;
+ gboolean can_write_mode;
+
+ can_write_theme = mateconf_client_key_is_writable (client,
+ KEY_THEMES,
+ NULL);
+ can_write_mode = mateconf_client_key_is_writable (client,
+ KEY_MODE,
+ NULL);
+ *is_writable = can_write_theme && can_write_mode;
+ }
+
+ mode = config_get_mode (NULL);
+
+ name = NULL;
+ if (mode == GS_MODE_BLANK_ONLY)
+ {
+ name = g_strdup ("__blank-only");
+ }
+ else if (mode == GS_MODE_RANDOM)
+ {
+ name = g_strdup ("__random");
+ }
+ else
+ {
+ GSList *list;
+ list = mateconf_client_get_list (client,
+ KEY_THEMES,
+ MATECONF_VALUE_STRING,
+ NULL);
+ if (list != NULL)
+ {
+ name = g_strdup (list->data);
+ }
+ else
+ {
+ /* TODO: handle error */
+ /* default to blank */
+ name = g_strdup ("__blank-only");
+ }
+
+ g_slist_foreach (list, (GFunc)g_free, NULL);
+ g_slist_free (list);
+ }
+
+ g_object_unref (client);
+
+ return name;
+}
+
+static GSList *
+get_all_theme_ids (GSThemeManager *theme_manager)
+{
+ GSList *ids = NULL;
+ GSList *entries;
+ GSList *l;
+
+ entries = gs_theme_manager_get_info_list (theme_manager);
+ for (l = entries; l; l = l->next)
+ {
+ GSThemeInfo *info = l->data;
+
+ ids = g_slist_prepend (ids, g_strdup (gs_theme_info_get_id (info)));
+ gs_theme_info_unref (info);
+ }
+ g_slist_free (entries);
+
+ return ids;
+}
+
+static void
+config_set_theme (const char *theme_id)
+{
+ MateConfClient *client;
+ GSList *list = NULL;
+ int mode;
+
+ client = mateconf_client_get_default ();
+
+ if (theme_id && strcmp (theme_id, "__blank-only") == 0)
+ {
+ mode = GS_MODE_BLANK_ONLY;
+ }
+ else if (theme_id && strcmp (theme_id, "__random") == 0)
+ {
+ mode = GS_MODE_RANDOM;
+
+ /* set the themes key to contain all available screensavers */
+ list = get_all_theme_ids (theme_manager);
+ }
+ else
+ {
+ mode = GS_MODE_SINGLE;
+ list = g_slist_append (list, g_strdup (theme_id));
+ }
+
+ config_set_mode (mode);
+
+ mateconf_client_set_list (client,
+ KEY_THEMES,
+ MATECONF_VALUE_STRING,
+ list,
+ NULL);
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+
+ g_object_unref (client);
+}
+
+static gboolean
+config_get_enabled (gboolean *is_writable)
+{
+ int enabled;
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_LOCK,
+ NULL);
+ }
+
+ enabled = mateconf_client_get_bool (client, KEY_IDLE_ACTIVATION_ENABLED, NULL);
+
+ g_object_unref (client);
+
+ return enabled;
+}
+
+static void
+config_set_enabled (gboolean enabled)
+{
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_set_bool (client, KEY_IDLE_ACTIVATION_ENABLED, enabled, NULL);
+
+ g_object_unref (client);
+}
+
+static gboolean
+config_get_lock (gboolean *is_writable)
+{
+ MateConfClient *client;
+ gboolean lock;
+
+ client = mateconf_client_get_default ();
+
+ if (is_writable)
+ {
+ *is_writable = mateconf_client_key_is_writable (client,
+ KEY_LOCK,
+ NULL);
+ }
+
+ lock = mateconf_client_get_bool (client, KEY_LOCK, NULL);
+
+ g_object_unref (client);
+
+ return lock;
+}
+
+static gboolean
+config_get_lock_disabled ()
+{
+ MateConfClient *client;
+ gboolean lock;
+
+ client = mateconf_client_get_default ();
+
+ lock = mateconf_client_get_bool (client, KEY_LOCK_DISABLE, NULL);
+
+ g_object_unref (client);
+ return lock;
+}
+
+static void
+config_set_lock (gboolean lock)
+{
+ MateConfClient *client;
+
+ client = mateconf_client_get_default ();
+
+ mateconf_client_set_bool (client, KEY_LOCK, lock, NULL);
+
+ g_object_unref (client);
+}
+
+static void
+preview_clear (GtkWidget *widget)
+{
+ GdkColor color = { 0, 0, 0 };
+
+ gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, &color);
+ gdk_window_clear (widget->window);
+}
+
+static void
+job_set_theme (GSJob *job,
+ const char *theme)
+{
+ GSThemeInfo *info;
+ const char *command;
+
+ command = NULL;
+
+ info = gs_theme_manager_lookup_theme_info (theme_manager, theme);
+ if (info != NULL)
+ {
+ command = gs_theme_info_get_exec (info);
+ }
+
+ gs_job_set_command (job, command);
+
+ if (info != NULL)
+ {
+ gs_theme_info_unref (info);
+ }
+}
+
+static void
+preview_set_theme (GtkWidget *widget,
+ const char *theme,
+ const char *name)
+{
+ GtkWidget *label;
+ char *markup;
+
+ if (job != NULL)
+ {
+ gs_job_stop (job);
+ }
+
+ preview_clear (widget);
+
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_theme_label"));
+ markup = g_markup_printf_escaped ("<i>%s</i>", name);
+ gtk_label_set_markup (GTK_LABEL (label), markup);
+ g_free (markup);
+
+ if ((theme && strcmp (theme, "__blank-only") == 0))
+ {
+
+ }
+ else if (theme && strcmp (theme, "__random") == 0)
+ {
+ GSList *themes;
+
+ themes = get_all_theme_ids (theme_manager);
+ if (themes != NULL)
+ {
+ GSList *l;
+ gint32 i;
+
+ i = g_random_int_range (0, g_slist_length (themes));
+ l = g_slist_nth (themes, i);
+
+ job_set_theme (job, (const char *) l->data);
+ g_slist_foreach (themes, (GFunc) g_free, NULL);
+ g_slist_free (themes);
+
+ gs_job_start (job);
+ }
+ }
+ else
+ {
+ job_set_theme (job, theme);
+ gs_job_start (job);
+ }
+}
+
+static void
+help_display (void)
+{
+ GError *error = NULL;
+ char *command;
+ const char *lang;
+ char *uri = NULL;
+ GdkScreen *gscreen;
+ int i;
+
+ const char * const * langs = g_get_language_names ();
+
+ for (i = 0; langs[i] != NULL; i++)
+ {
+ lang = langs[i];
+ if (strchr (lang, '.'))
+ {
+ continue;
+ }
+
+ uri = g_build_filename (DATADIR,
+ "/mate/help/user-guide/",
+ lang,
+ "/user-guide.xml",
+ NULL);
+
+ if (g_file_test (uri, G_FILE_TEST_EXISTS))
+ {
+ break;
+ }
+ }
+
+ command = g_strconcat ("mate-open ghelp://",
+ uri,
+ "?prefs-screensaver",
+ NULL);
+ gscreen = gdk_screen_get_default ();
+ gdk_spawn_command_line_on_screen (gscreen, command, &error);
+
+ if (error != NULL)
+ {
+ GtkWidget *d;
+
+ d = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "%s", error->message);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (d);
+ g_error_free (error);
+ }
+
+ g_free (command);
+ g_free (uri);
+}
+
+static void
+response_cb (GtkWidget *widget,
+ int response_id)
+{
+
+ if (response_id == GTK_RESPONSE_HELP)
+ {
+ help_display ();
+ }
+ else if (response_id == GTK_RESPONSE_REJECT)
+ {
+ GError *error;
+ gboolean res;
+
+ error = NULL;
+
+ res = gdk_spawn_command_line_on_screen (gdk_screen_get_default (),
+ GPM_COMMAND,
+ &error);
+ if (! res)
+ {
+ g_warning ("Unable to start power management preferences: %s", error->message);
+ g_error_free (error);
+ }
+ }
+ else
+ {
+ gtk_widget_destroy (widget);
+ gtk_main_quit ();
+ }
+}
+
+static GSList *
+get_theme_info_list (void)
+{
+ return gs_theme_manager_get_info_list (theme_manager);
+}
+
+static void
+populate_model (GtkTreeStore *store)
+{
+ GtkTreeIter iter;
+ GSList *themes = NULL;
+ GSList *l;
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, _("Blank screen"),
+ ID_COLUMN, "__blank-only",
+ -1);
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, _("Random"),
+ ID_COLUMN, "__random",
+ -1);
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, NULL,
+ ID_COLUMN, "__separator",
+ -1);
+
+ themes = get_theme_info_list ();
+
+ if (themes == NULL)
+ {
+ return;
+ }
+
+ for (l = themes; l; l = l->next)
+ {
+ const char *name;
+ const char *id;
+ GSThemeInfo *info = l->data;
+
+ if (info == NULL)
+ {
+ continue;
+ }
+
+ name = gs_theme_info_get_name (info);
+ id = gs_theme_info_get_id (info);
+
+ gtk_tree_store_append (store, &iter, NULL);
+ gtk_tree_store_set (store, &iter,
+ NAME_COLUMN, name,
+ ID_COLUMN, id,
+ -1);
+
+ gs_theme_info_unref (info);
+ }
+
+ g_slist_free (themes);
+}
+
+static void
+tree_selection_previous (GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ if (! gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ return;
+ }
+
+ path = gtk_tree_model_get_path (model, &iter);
+ if (gtk_tree_path_prev (path))
+ {
+ gtk_tree_selection_select_path (selection, path);
+ }
+}
+
+static void
+tree_selection_next (GtkTreeSelection *selection)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+
+ if (! gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ return;
+ }
+
+ path = gtk_tree_model_get_path (model, &iter);
+ gtk_tree_path_next (path);
+ gtk_tree_selection_select_path (selection, path);
+}
+
+static void
+tree_selection_changed_cb (GtkTreeSelection *selection,
+ GtkWidget *preview)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ char *theme;
+ char *name;
+
+ if (! gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ return;
+ }
+
+ gtk_tree_model_get (model, &iter, ID_COLUMN, &theme, NAME_COLUMN, &name, -1);
+
+ if (theme == NULL)
+ {
+ g_free (name);
+ return;
+ }
+
+ preview_set_theme (preview, theme, name);
+ config_set_theme (theme);
+
+ g_free (theme);
+ g_free (name);
+}
+
+static void
+activate_delay_value_changed_cb (GtkRange *range,
+ gpointer user_data)
+{
+ gdouble value;
+
+ value = gtk_range_get_value (range);
+ config_set_activate_delay ((gint32)value);
+}
+
+static int
+compare_theme_names (char *name_a,
+ char *name_b,
+ char *id_a,
+ char *id_b)
+{
+
+ if (id_a == NULL)
+ {
+ return 1;
+ }
+ else if (id_b == NULL)
+ {
+ return -1;
+ }
+
+ if (strcmp (id_a, "__blank-only") == 0)
+ {
+ return -1;
+ }
+ else if (strcmp (id_b, "__blank-only") == 0)
+ {
+ return 1;
+ }
+ else if (strcmp (id_a, "__random") == 0)
+ {
+ return -1;
+ }
+ else if (strcmp (id_b, "__random") == 0)
+ {
+ return 1;
+ }
+ else if (strcmp (id_a, "__separator") == 0)
+ {
+ return -1;
+ }
+ else if (strcmp (id_b, "__separator") == 0)
+ {
+ return 1;
+ }
+
+ if (name_a == NULL)
+ {
+ return 1;
+ }
+ else if (name_b == NULL)
+ {
+ return -1;
+ }
+
+ return g_utf8_collate (name_a, name_b);
+}
+
+static int
+compare_theme (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ char *name_a;
+ char *name_b;
+ char *id_a;
+ char *id_b;
+ int result;
+
+ gtk_tree_model_get (model, a, NAME_COLUMN, &name_a, -1);
+ gtk_tree_model_get (model, b, NAME_COLUMN, &name_b, -1);
+ gtk_tree_model_get (model, a, ID_COLUMN, &id_a, -1);
+ gtk_tree_model_get (model, b, ID_COLUMN, &id_b, -1);
+
+ result = compare_theme_names (name_a, name_b, id_a, id_b);
+
+ g_free (name_a);
+ g_free (name_b);
+ g_free (id_a);
+ g_free (id_b);
+
+ return result;
+}
+
+static gboolean
+separator_func (GtkTreeModel *model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ int column = GPOINTER_TO_INT (data);
+ char *text;
+
+ gtk_tree_model_get (model, iter, column, &text, -1);
+
+ if (text != NULL && strcmp (text, "__separator") == 0)
+ {
+ return TRUE;
+ }
+
+ g_free (text);
+
+ return FALSE;
+}
+
+static void
+setup_treeview (GtkWidget *tree,
+ GtkWidget *preview)
+{
+ GtkTreeStore *store;
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *select;
+
+ store = gtk_tree_store_new (N_COLUMNS,
+ G_TYPE_STRING,
+ G_TYPE_STRING);
+ populate_model (store);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (tree),
+ GTK_TREE_MODEL (store));
+
+ g_object_unref (store);
+
+#if GTK_CHECK_VERSION(2,10,0)
+ g_object_set (tree, "show-expanders", FALSE, NULL);
+#endif
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes ("Name", renderer,
+ "text", NAME_COLUMN,
+ NULL);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
+
+ gtk_tree_view_column_set_sort_column_id (column, NAME_COLUMN);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (store),
+ NAME_COLUMN,
+ compare_theme,
+ NULL, NULL);
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store),
+ NAME_COLUMN,
+ GTK_SORT_ASCENDING);
+
+ gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (tree),
+ separator_func,
+ GINT_TO_POINTER (ID_COLUMN),
+ NULL);
+
+ select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
+ gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
+ g_signal_connect (G_OBJECT (select), "changed",
+ G_CALLBACK (tree_selection_changed_cb),
+ preview);
+
+}
+
+static void
+setup_treeview_selection (GtkWidget *tree)
+{
+ char *theme;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreePath *path = NULL;
+ gboolean is_writable;
+
+ theme = config_get_theme (&is_writable);
+
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (tree, FALSE);
+ }
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
+
+ if (theme && gtk_tree_model_get_iter_first (model, &iter))
+ {
+
+ do
+ {
+ char *id;
+ gboolean found;
+
+ gtk_tree_model_get (model, &iter,
+ ID_COLUMN, &id, -1);
+ found = (id && strcmp (id, theme) == 0);
+ g_free (id);
+
+ if (found)
+ {
+ path = gtk_tree_model_get_path (model, &iter);
+ break;
+ }
+
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ if (path)
+ {
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (tree),
+ path,
+ NULL,
+ FALSE);
+
+ gtk_tree_path_free (path);
+ }
+
+ g_free (theme);
+}
+
+static void
+reload_themes (void)
+{
+ GtkWidget *treeview;
+ GtkTreeModel *model;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (treeview));
+ gtk_tree_store_clear (GTK_TREE_STORE (model));
+ populate_model (GTK_TREE_STORE (model));
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (treeview),
+ GTK_TREE_MODEL (model));
+}
+
+static void
+theme_copy_complete_cb (GtkWidget *dialog, gpointer user_data)
+{
+ reload_themes ();
+ gtk_widget_destroy (dialog);
+}
+
+static void
+theme_installer_run (GtkWidget *prefs_dialog, GList *files)
+{
+ GtkWidget *copy_dialog;
+
+ copy_dialog = copy_theme_dialog_new (files);
+ g_list_foreach (files, (GFunc) (g_object_unref), NULL);
+ g_list_free (files);
+
+ gtk_window_set_transient_for (GTK_WINDOW (copy_dialog),
+ GTK_WINDOW (prefs_dialog));
+ gtk_window_set_icon_name (GTK_WINDOW (copy_dialog),
+ "preferences-desktop-screensaver");
+
+ g_signal_connect (copy_dialog, "complete",
+ G_CALLBACK (theme_copy_complete_cb), NULL);
+
+ copy_theme_dialog_begin (COPY_THEME_DIALOG (copy_dialog));
+}
+
+/* Callback issued during drag movements */
+static gboolean
+drag_motion_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ guint time,
+ gpointer data)
+{
+ return FALSE;
+}
+
+/* Callback issued during drag leaves */
+static void
+drag_leave_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time,
+ gpointer data)
+{
+ gtk_widget_queue_draw (widget);
+}
+
+/* GIO has no version of mate_vfs_uri_list_parse(), so copy from MateVFS
+ * and re-work to create GFiles.
+**/
+static GList *
+uri_list_parse (const gchar *uri_list)
+{
+ const gchar *p, *q;
+ gchar *retval;
+ GFile *file;
+ GList *result = NULL;
+
+ g_return_val_if_fail (uri_list != NULL, NULL);
+
+ p = uri_list;
+
+ /* We don't actually try to validate the URI according to RFC
+ * 2396, or even check for allowed characters - we just ignore
+ * comments and trim whitespace off the ends. We also
+ * allow LF delimination as well as the specified CRLF.
+ */
+ while (p != NULL)
+ {
+ if (*p != '#')
+ {
+ while (g_ascii_isspace (*p))
+ p++;
+
+ q = p;
+ while ((*q != '\0')
+ && (*q != '\n')
+ && (*q != '\r'))
+ q++;
+
+ if (q > p)
+ {
+ q--;
+ while (q > p
+ && g_ascii_isspace (*q))
+ q--;
+
+ retval = g_malloc (q - p + 2);
+ strncpy (retval, p, q - p + 1);
+ retval[q - p + 1] = '\0';
+
+ file = g_file_new_for_uri (retval);
+
+ g_free (retval);
+
+ if (file != NULL)
+ result = g_list_prepend (result, file);
+ }
+ }
+ p = strchr (p, '\n');
+ if (p != NULL)
+ p++;
+ }
+
+ return g_list_reverse (result);
+}
+
+/* Callback issued on actual drops. Attempts to load the file dropped. */
+static void
+drag_data_received_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ int x,
+ int y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ GList *files;
+
+ if (!(info == TARGET_URI_LIST || info == TARGET_NS_URL))
+ return;
+
+ files = uri_list_parse ((char *) selection_data->data);
+ if (files != NULL)
+ {
+ GtkWidget *prefs_dialog;
+
+ prefs_dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ theme_installer_run (prefs_dialog, files);
+ }
+}
+
+/* Adapted from totem_time_to_string_text */
+static char *
+time_to_string_text (long time)
+{
+ char *secs, *mins, *hours, *string;
+ int sec, min, hour;
+
+ sec = time % 60;
+ time = time - sec;
+ min = (time % (60 * 60)) / 60;
+ time = time - (min * 60);
+ hour = time / (60 * 60);
+
+ hours = g_strdup_printf (ngettext ("%d hour",
+ "%d hours", hour), hour);
+
+ mins = g_strdup_printf (ngettext ("%d minute",
+ "%d minutes", min), min);
+
+ secs = g_strdup_printf (ngettext ("%d second",
+ "%d seconds", sec), sec);
+
+ if (hour > 0)
+ {
+ if (sec > 0)
+ {
+ /* hour:minutes:seconds */
+ string = g_strdup_printf (_("%s %s %s"), hours, mins, secs);
+ }
+ else if (min > 0)
+ {
+ /* hour:minutes */
+ string = g_strdup_printf (_("%s %s"), hours, mins);
+ }
+ else
+ {
+ /* hour */
+ string = g_strdup_printf (_("%s"), hours);
+ }
+ }
+ else if (min > 0)
+ {
+ if (sec > 0)
+ {
+ /* minutes:seconds */
+ string = g_strdup_printf (_("%s %s"), mins, secs);
+ }
+ else
+ {
+ /* minutes */
+ string = g_strdup_printf (_("%s"), mins);
+ }
+ }
+ else
+ {
+ /* seconds */
+ string = g_strdup_printf (_("%s"), secs);
+ }
+
+ g_free (hours);
+ g_free (mins);
+ g_free (secs);
+
+ return string;
+}
+
+static char *
+format_value_callback_time (GtkScale *scale,
+ gdouble value)
+{
+ if (value == 0)
+ return g_strdup_printf (_("Never"));
+
+ return time_to_string_text (value * 60.0);
+}
+
+static void
+lock_checkbox_toggled (GtkToggleButton *button, gpointer user_data)
+{
+ config_set_lock (gtk_toggle_button_get_active (button));
+}
+
+static void
+enabled_checkbox_toggled (GtkToggleButton *button, gpointer user_data)
+{
+ config_set_enabled (gtk_toggle_button_get_active (button));
+}
+
+static void
+invalid_type_warning (const char *type)
+{
+ g_warning ("Error retrieving configuration key '%s': Invalid type",
+ type);
+}
+
+static void
+ui_disable_lock (gboolean disable)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ gtk_widget_set_sensitive (widget, !disable);
+ if (disable)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE);
+ }
+}
+
+static void
+ui_set_lock (gboolean enabled)
+{
+ GtkWidget *widget;
+ gboolean active;
+ gboolean lock_disabled;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ if (active != enabled)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), enabled);
+ }
+ lock_disabled = config_get_lock_disabled ();
+ ui_disable_lock (lock_disabled);
+}
+
+static void
+ui_set_enabled (gboolean enabled)
+{
+ GtkWidget *widget;
+ gboolean active;
+ gboolean is_writable;
+ gboolean lock_disabled;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "enable_checkbox"));
+ active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ if (active != enabled)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), enabled);
+ }
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ config_get_lock (&is_writable);
+ if (is_writable)
+ {
+ gtk_widget_set_sensitive (widget, enabled);
+ }
+ lock_disabled = config_get_lock_disabled ();
+ ui_disable_lock(lock_disabled);
+}
+
+static void
+ui_set_delay (int delay)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_hscale"));
+ gtk_range_set_value (GTK_RANGE (widget), delay);
+}
+
+static void
+key_changed_cb (MateConfClient *client,
+ guint cnxn_id,
+ MateConfEntry *entry,
+ gpointer data)
+{
+ const char *key;
+ MateConfValue *value;
+
+ key = mateconf_entry_get_key (entry);
+
+ if (! g_str_has_prefix (key, KEY_DIR) && ! g_str_has_prefix (key, MATE_LOCKDOWN_DIR))
+ {
+ return;
+ }
+
+ value = mateconf_entry_get_value (entry);
+
+ if (strcmp (key, KEY_IDLE_ACTIVATION_ENABLED) == 0)
+ {
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+
+ ui_set_enabled (enabled);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_LOCK) == 0)
+ {
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean enabled;
+
+ enabled = mateconf_value_get_bool (value);
+
+ ui_set_lock (enabled);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_LOCK_DISABLE) == 0)
+ {
+ if (value->type == MATECONF_VALUE_BOOL)
+ {
+ gboolean disabled;
+
+ disabled = mateconf_value_get_bool (value);
+
+ ui_disable_lock (disabled);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_THEMES) == 0)
+ {
+ if (value->type == MATECONF_VALUE_LIST)
+ {
+ GtkWidget *treeview;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ setup_treeview_selection (treeview);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+ }
+ else if (strcmp (key, KEY_ACTIVATE_DELAY) == 0)
+ {
+
+ if (value->type == MATECONF_VALUE_INT)
+ {
+ int delay;
+
+ delay = mateconf_value_get_int (value);
+ ui_set_delay (delay);
+ }
+ else
+ {
+ invalid_type_warning (key);
+ }
+
+ }
+ else
+ {
+ /*g_warning ("Config key not handled: %s", key);*/
+ }
+}
+
+static void
+fullscreen_preview_previous_cb (GtkWidget *fullscreen_preview_window,
+ gpointer user_data)
+{
+ GtkWidget *treeview;
+ GtkTreeSelection *selection;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+ tree_selection_previous (selection);
+}
+
+static void
+fullscreen_preview_next_cb (GtkWidget *fullscreen_preview_window,
+ gpointer user_data)
+{
+ GtkWidget *treeview;
+ GtkTreeSelection *selection;
+
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
+ tree_selection_next (selection);
+}
+
+static void
+fullscreen_preview_cancelled_cb (GtkWidget *button,
+ gpointer user_data)
+{
+
+ GtkWidget *fullscreen_preview_area;
+ GtkWidget *fullscreen_preview_window;
+ GtkWidget *preview_area;
+ GtkWidget *dialog;
+
+ preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "preview_area"));
+ gs_job_set_widget (job, preview_area);
+
+ fullscreen_preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_area"));
+ preview_clear (fullscreen_preview_area);
+
+ fullscreen_preview_window = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_window"));
+ gtk_widget_hide (fullscreen_preview_window);
+
+ dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ gtk_widget_show (dialog);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+fullscreen_preview_start_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkWidget *fullscreen_preview_area;
+ GtkWidget *fullscreen_preview_window;
+ GtkWidget *dialog;
+
+ dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ gtk_widget_hide (dialog);
+
+ fullscreen_preview_window = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_window"));
+
+ gtk_window_fullscreen (GTK_WINDOW (fullscreen_preview_window));
+ gtk_window_set_keep_above (GTK_WINDOW (fullscreen_preview_window), TRUE);
+
+ gtk_widget_show (fullscreen_preview_window);
+ gtk_widget_grab_focus (fullscreen_preview_window);
+
+ fullscreen_preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_area"));
+ preview_clear (fullscreen_preview_area);
+ gs_job_set_widget (job, fullscreen_preview_area);
+}
+
+static void
+constrain_list_size (GtkWidget *widget,
+ GtkRequisition *requisition,
+ GtkWidget *to_size)
+{
+ GtkRequisition req;
+ int max_height;
+
+ /* constrain height to be the tree height up to a max */
+ max_height = (gdk_screen_get_height (gtk_widget_get_screen (widget))) / 4;
+
+ gtk_widget_size_request (to_size, &req);
+
+ requisition->height = MIN (req.height, max_height);
+}
+
+static void
+setup_list_size_constraint (GtkWidget *widget,
+ GtkWidget *to_size)
+{
+ g_signal_connect (widget, "size-request",
+ G_CALLBACK (constrain_list_size), to_size);
+}
+
+static gboolean
+check_is_root_user (void)
+{
+#ifndef G_OS_WIN32
+ uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
+ gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
+
+#ifdef HAVE_GETRESUID
+ if (getresuid (&ruid, &euid, &suid) != 0 ||
+ getresgid (&rgid, &egid, &sgid) != 0)
+#endif /* HAVE_GETRESUID */
+ {
+ suid = ruid = getuid ();
+ sgid = rgid = getgid ();
+ euid = geteuid ();
+ egid = getegid ();
+ }
+
+ if (ruid == 0)
+ {
+ return TRUE;
+ }
+
+#endif
+ return FALSE;
+}
+
+static void
+setup_for_root_user (void)
+{
+ GtkWidget *lock_checkbox;
+ GtkWidget *label;
+
+ lock_checkbox = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "root_warning_label"));
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lock_checkbox), FALSE);
+ gtk_widget_set_sensitive (lock_checkbox, FALSE);
+
+ gtk_widget_show (label);
+}
+
+static GdkVisual *
+get_best_visual (void)
+{
+ char *command;
+ char *std_output;
+ int exit_status;
+ GError *error;
+ unsigned long v;
+ char c;
+ GdkVisual *visual;
+ gboolean res;
+
+ visual = NULL;
+
+ command = g_build_filename (LIBEXECDIR, "mate-screensaver-gl-helper", NULL);
+
+ error = NULL;
+ res = g_spawn_command_line_sync (command,
+ &std_output,
+ NULL,
+ &exit_status,
+ &error);
+
+ if (! res)
+ {
+ g_debug ("Could not run command '%s': %s", command, error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (1 == sscanf (std_output, "0x%lx %c", &v, &c))
+ {
+ if (v != 0)
+ {
+ VisualID visual_id;
+
+ visual_id = (VisualID) v;
+ visual = gdkx_visual_get (visual_id);
+
+ g_debug ("Found best visual for GL: 0x%x",
+ (unsigned int) visual_id);
+ }
+ }
+
+out:
+ g_free (std_output);
+ g_free (command);
+
+ return visual;
+}
+
+static GdkColormap *
+get_best_colormap_for_screen (GdkScreen *screen)
+{
+ GdkColormap *colormap;
+ GdkVisual *visual;
+
+ g_return_val_if_fail (screen != NULL, NULL);
+
+ visual = get_best_visual ();
+
+ colormap = NULL;
+ if (visual != NULL)
+ {
+ colormap = gdk_colormap_new (visual, FALSE);
+ }
+
+ return colormap;
+}
+
+static void
+widget_set_best_colormap (GtkWidget *widget)
+{
+ GdkColormap *colormap;
+
+ g_return_if_fail (widget != NULL);
+
+ colormap = get_best_colormap_for_screen (gtk_widget_get_screen (widget));
+ if (colormap != NULL)
+ {
+ gtk_widget_set_colormap (widget, colormap);
+ g_object_unref (colormap);
+ }
+}
+
+static gboolean
+setup_treeview_idle (gpointer data)
+{
+ GtkWidget *preview;
+ GtkWidget *treeview;
+
+ preview = GTK_WIDGET (gtk_builder_get_object (builder, "preview_area"));
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+
+ setup_treeview (treeview, preview);
+ setup_treeview_selection (treeview);
+
+ return FALSE;
+}
+
+static gboolean
+is_program_in_path (const char *program)
+{
+ char *tmp = g_find_program_in_path (program);
+ if (tmp != NULL)
+ {
+ g_free (tmp);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static void
+init_capplet (void)
+{
+ GtkWidget *dialog;
+ GtkWidget *preview;
+ GtkWidget *treeview;
+ GtkWidget *list_scroller;
+ GtkWidget *activate_delay_hscale;
+ GtkWidget *activate_delay_hbox;
+ GtkWidget *label;
+ GtkWidget *enabled_checkbox;
+ GtkWidget *lock_checkbox;
+ GtkWidget *root_warning_label;
+ GtkWidget *preview_button;
+ GtkWidget *gpm_button;
+ GtkWidget *fullscreen_preview_window;
+ GtkWidget *fullscreen_preview_previous;
+ GtkWidget *fullscreen_preview_next;
+ GtkWidget *fullscreen_preview_area;
+ GtkWidget *fullscreen_preview_close;
+ char *gtk_builder_file;
+ char *string;
+ gdouble activate_delay;
+ gboolean enabled;
+ gboolean is_writable;
+ MateConfClient *client;
+ GError *error=NULL;
+
+ gtk_builder_file = g_build_filename (GTKBUILDERDIR, GTK_BUILDER_FILE, NULL);
+ builder = gtk_builder_new();
+ if (!gtk_builder_add_from_file(builder, gtk_builder_file, &error))
+ {
+ g_warning("Couldn't load builder file: %s", error->message);
+ g_error_free(error);
+ }
+ g_free (gtk_builder_file);
+
+ if (builder == NULL)
+ {
+
+ dialog = gtk_message_dialog_new (NULL,
+ 0, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Could not load the main interface"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ _("Please make sure that the screensaver is properly installed"));
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog),
+ GTK_RESPONSE_OK);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+ exit (1);
+ }
+
+ preview = GTK_WIDGET (gtk_builder_get_object (builder, "preview_area"));
+ dialog = GTK_WIDGET (gtk_builder_get_object (builder, "prefs_dialog"));
+ treeview = GTK_WIDGET (gtk_builder_get_object (builder, "savers_treeview"));
+ list_scroller = GTK_WIDGET (gtk_builder_get_object (builder, "themes_scrolled_window"));
+ activate_delay_hscale = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_hscale"));
+ activate_delay_hbox = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_hbox"));
+ enabled_checkbox = GTK_WIDGET (gtk_builder_get_object (builder, "enable_checkbox"));
+ lock_checkbox = GTK_WIDGET (gtk_builder_get_object (builder, "lock_checkbox"));
+ root_warning_label = GTK_WIDGET (gtk_builder_get_object (builder, "root_warning_label"));
+ preview_button = GTK_WIDGET (gtk_builder_get_object (builder, "preview_button"));
+ gpm_button = GTK_WIDGET (gtk_builder_get_object (builder, "gpm_button"));
+ fullscreen_preview_window = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_window"));
+ fullscreen_preview_area = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_area"));
+ fullscreen_preview_close = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_close"));
+ fullscreen_preview_previous = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_previous_button"));
+ fullscreen_preview_next = GTK_WIDGET (gtk_builder_get_object (builder, "fullscreen_preview_next_button"));
+
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "activate_delay_label"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), activate_delay_hscale);
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "savers_label"));
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), treeview);
+
+ gtk_widget_set_no_show_all (root_warning_label, TRUE);
+ widget_set_best_colormap (preview);
+
+ if (! is_program_in_path (GPM_COMMAND))
+ {
+ gtk_widget_set_no_show_all (gpm_button, TRUE);
+ gtk_widget_hide (gpm_button);
+ }
+
+ activate_delay = config_get_activate_delay (&is_writable);
+ ui_set_delay (activate_delay);
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (activate_delay_hbox, FALSE);
+ }
+ g_signal_connect (activate_delay_hscale, "format-value",
+ G_CALLBACK (format_value_callback_time), NULL);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lock_checkbox), config_get_lock (&is_writable));
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (lock_checkbox, FALSE);
+ }
+ g_signal_connect (lock_checkbox, "toggled",
+ G_CALLBACK (lock_checkbox_toggled), NULL);
+
+ enabled = config_get_enabled (&is_writable);
+ ui_set_enabled (enabled);
+ if (! is_writable)
+ {
+ gtk_widget_set_sensitive (enabled_checkbox, FALSE);
+ }
+ g_signal_connect (enabled_checkbox, "toggled",
+ G_CALLBACK (enabled_checkbox_toggled), NULL);
+
+ setup_list_size_constraint (list_scroller, treeview);
+ gtk_widget_set_size_request (preview, 480, 300);
+ gtk_window_set_icon_name (GTK_WINDOW (dialog), "preferences-desktop-screensaver");
+ gtk_window_set_icon_name (GTK_WINDOW (fullscreen_preview_window), "screensaver");
+
+ gtk_drag_dest_set (dialog, GTK_DEST_DEFAULT_ALL,
+ drop_types, G_N_ELEMENTS (drop_types),
+ GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE);
+
+ g_signal_connect (dialog, "drag-motion",
+ G_CALLBACK (drag_motion_cb), NULL);
+ g_signal_connect (dialog, "drag-leave",
+ G_CALLBACK (drag_leave_cb), NULL);
+ g_signal_connect (dialog, "drag-data-received",
+ G_CALLBACK (drag_data_received_cb), NULL);
+
+ gtk_widget_show_all (dialog);
+
+ /* Update list of themes if using random screensaver */
+ client = mateconf_client_get_default ();
+ string = mateconf_client_get_string (client, KEY_MODE, NULL);
+ if (string != NULL)
+ {
+ int mode;
+ GSList *list;
+
+ mateconf_string_to_enum (mode_enum_map, string, &mode);
+ g_free (string);
+
+ if (mode == GS_MODE_RANDOM)
+ {
+ list = get_all_theme_ids (theme_manager);
+ mateconf_client_set_list (client, KEY_THEMES, MATECONF_VALUE_STRING, list, NULL);
+
+ g_slist_foreach (list, (GFunc) g_free, NULL);
+ g_slist_free (list);
+ }
+ }
+
+ mateconf_client_add_dir (client, KEY_DIR,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_notify_add (client,
+ KEY_DIR,
+ key_changed_cb,
+ NULL, NULL, NULL);
+ mateconf_client_add_dir (client, MATE_LOCKDOWN_DIR,
+ MATECONF_CLIENT_PRELOAD_ONELEVEL,
+ NULL);
+ mateconf_client_notify_add (client,
+ MATE_LOCKDOWN_DIR,
+ key_changed_cb,
+ NULL, NULL, NULL);
+
+ g_object_unref (client);
+
+ preview_clear (preview);
+ gs_job_set_widget (job, preview);
+
+ if (check_is_root_user ())
+ {
+ setup_for_root_user ();
+ }
+
+ g_signal_connect (activate_delay_hscale, "value-changed",
+ G_CALLBACK (activate_delay_value_changed_cb), NULL);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (response_cb), NULL);
+
+ g_signal_connect (preview_button, "clicked",
+ G_CALLBACK (fullscreen_preview_start_cb),
+ treeview);
+
+ g_signal_connect (fullscreen_preview_close, "clicked",
+ G_CALLBACK (fullscreen_preview_cancelled_cb), NULL);
+ g_signal_connect (fullscreen_preview_previous, "clicked",
+ G_CALLBACK (fullscreen_preview_previous_cb), NULL);
+ g_signal_connect (fullscreen_preview_next, "clicked",
+ G_CALLBACK (fullscreen_preview_next_cb), NULL);
+
+ g_idle_add ((GSourceFunc)setup_treeview_idle, NULL);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ gtk_init (&argc, &argv);
+
+ job = gs_job_new ();
+ theme_manager = gs_theme_manager_new ();
+
+ init_capplet ();
+
+ gtk_main ();
+
+ g_object_unref (theme_manager);
+ g_object_unref (job);
+
+ return 0;
+}
diff --git a/src/mate-screensaver.c b/src/mate-screensaver.c
new file mode 100644
index 0000000..1e76fe6
--- /dev/null
+++ b/src/mate-screensaver.c
@@ -0,0 +1,132 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "mate-screensaver.h"
+#include "gs-monitor.h"
+#include "gs-debug.h"
+
+void
+mate_screensaver_quit (void)
+{
+ gtk_main_quit ();
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GSMonitor *monitor;
+ GError *error = NULL;
+ static gboolean show_version = FALSE;
+ static gboolean no_daemon = FALSE;
+ static gboolean debug = FALSE;
+ static GOptionEntry entries [] =
+ {
+ { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+ { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &no_daemon, N_("Don't become a daemon"), NULL },
+ { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL },
+ { NULL }
+ };
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, entries, NULL, &error))
+ {
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("Unable to initialize GTK+");
+ }
+ exit (1);
+ }
+
+ if (show_version)
+ {
+ g_print ("%s %s\n", argv [0], VERSION);
+ exit (1);
+ }
+
+ /* debug to a file if in deamon mode */
+ gs_debug_init (debug, ! no_daemon);
+ gs_debug ("initializing mate-screensaver %s", VERSION);
+
+ monitor = gs_monitor_new ();
+
+ if (monitor == NULL)
+ {
+ exit (1);
+ }
+
+ error = NULL;
+ if (! gs_monitor_start (monitor, &error))
+ {
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("Unable to start screensaver");
+ }
+ exit (1);
+ }
+
+ /* Don't close stdout and stderr for now */
+ if (! no_daemon && daemon (0, 1))
+ {
+ g_error ("Could not daemonize: %s", g_strerror (errno));
+ }
+
+ gtk_main ();
+
+ g_object_unref (monitor);
+
+ gs_debug ("mate-screensaver finished");
+
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/mate-screensaver.c.orig b/src/mate-screensaver.c.orig
new file mode 100644
index 0000000..456d377
--- /dev/null
+++ b/src/mate-screensaver.c.orig
@@ -0,0 +1,132 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * Copyright (C) 2004-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "mate-screensaver.h"
+#include "gs-monitor.h"
+#include "gs-debug.h"
+
+void
+mate_screensaver_quit (void)
+{
+ gtk_main_quit ();
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GSMonitor *monitor;
+ GError *error = NULL;
+ static gboolean show_version = FALSE;
+ static gboolean no_daemon = FALSE;
+ static gboolean debug = FALSE;
+ static GOptionEntry entries [] =
+ {
+ { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL },
+ { "no-daemon", 0, 0, G_OPTION_ARG_NONE, &no_daemon, N_("Don't become a daemon"), NULL },
+ { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL },
+ { NULL }
+ };
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, entries, NULL, &error))
+ {
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("Unable to initialize GTK+");
+ }
+ exit (1);
+ }
+
+ if (show_version)
+ {
+ g_print ("%s %s\n", argv [0], VERSION);
+ exit (1);
+ }
+
+ /* debug to a file if in deamon mode */
+ gs_debug_init (debug, ! no_daemon);
+ gs_debug ("initializing mate-screensaver %s", VERSION);
+
+ monitor = gs_monitor_new ();
+
+ if (monitor == NULL)
+ {
+ exit (1);
+ }
+
+ error = NULL;
+ if (! gs_monitor_start (monitor, &error))
+ {
+ if (error)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+ else
+ {
+ g_warning ("Unable to start screensaver");
+ }
+ exit (1);
+ }
+
+ /* Don't close stdout and stderr for now */
+ if (! no_daemon && daemon (0, 1))
+ {
+ g_error ("Could not daemonize: %s", g_strerror (errno));
+ }
+
+ gtk_main ();
+
+ g_object_unref (monitor);
+
+ gs_debug ("mate-screensaver finished");
+
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/mate-screensaver.desktop.in.in b/src/mate-screensaver.desktop.in.in
new file mode 100644
index 0000000..8aaeb18
--- /dev/null
+++ b/src/mate-screensaver.desktop.in.in
@@ -0,0 +1,14 @@
+[Desktop Entry]
+Type=Application
+_Name=Screensaver
+_Comment=Launch screen saver and locker program
+Icon=preferences-desktop-screensaver
+Exec=mate-screensaver
+OnlyShowIn=MATE;
+NoDisplay=true
+X-MATE-Autostart-Phase=Application
+X-MATE-Autostart-Notify=true
+X-MATE-Bugzilla-Bugzilla=MATE
+X-MATE-Bugzilla-Product=mate-screensaver
+X-MATE-Bugzilla-Component=general
+X-MATE-Bugzilla-Version=@VERSION@
diff --git a/src/mate-screensaver.h b/src/mate-screensaver.h
new file mode 100644
index 0000000..34b3a7c
--- /dev/null
+++ b/src/mate-screensaver.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#ifndef __MATE_SCREENSAVER_H
+#define __MATE_SCREENSAVER_H
+
+G_BEGIN_DECLS
+
+void mate_screensaver_quit (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/setuid.c b/src/setuid.c
new file mode 100644
index 0000000..80e4659
--- /dev/null
+++ b/src/setuid.c
@@ -0,0 +1,247 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * setuid.c --- management of runtime privileges.
+ *
+ * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "config.h"
+
+#ifndef EPERM
+#include <errno.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h> /* for getpwnam() and struct passwd */
+#include <grp.h> /* for getgrgid() and struct group */
+#include <glib.h>
+
+#include "setuid.h"
+
+static char *
+uid_gid_string (uid_t uid,
+ gid_t gid)
+{
+ static char *buf;
+ struct passwd *p = NULL;
+ struct group *g = NULL;
+
+ p = getpwuid (uid);
+ g = getgrgid (gid);
+
+ buf = g_strdup_printf ("%s/%s (%ld/%ld)",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) uid, (long) gid);
+
+ return buf;
+}
+
+static gboolean
+set_ids_by_number (uid_t uid,
+ gid_t gid,
+ char **message_ret)
+{
+ int uid_errno = 0;
+ int gid_errno = 0;
+ int sgs_errno = 0;
+ struct passwd *p = getpwuid (uid);
+ struct group *g = getgrgid (gid);
+
+ if (message_ret)
+ *message_ret = NULL;
+
+ /* Rumor has it that some implementations of of setuid() do nothing
+ when called with -1; therefore, if the "nobody" user has a uid of
+ -1, then that would be Really Bad. Rumor further has it that such
+ systems really ought to be using -2 for "nobody", since that works.
+ So, if we get a uid (or gid, for good measure) of -1, switch to -2
+ instead. Note that this must be done after we've looked up the
+ user/group names with getpwuid(-1) and/or getgrgid(-1).
+ */
+ if (gid == (gid_t) -1) gid = (gid_t) -2;
+ if (uid == (uid_t) -1) uid = (uid_t) -2;
+
+ errno = 0;
+ if (setgroups (1, &gid) < 0)
+ sgs_errno = errno ? errno : -1;
+
+ errno = 0;
+ if (setgid (gid) != 0)
+ gid_errno = errno ? errno : -1;
+
+ errno = 0;
+ if (setuid (uid) != 0)
+ uid_errno = errno ? errno : -1;
+
+ if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0)
+ {
+ static char *reason;
+ reason = g_strdup_printf ("changed uid/gid to %s/%s (%ld/%ld).",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) uid, (long) gid);
+ if (message_ret)
+ *message_ret = g_strdup (reason);
+
+ g_free (reason);
+
+ return TRUE;
+ }
+ else
+ {
+ char *reason = NULL;
+
+ if (sgs_errno)
+ {
+ reason = g_strdup_printf ("couldn't setgroups to %s (%ld)",
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) gid);
+ if (sgs_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = sgs_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+
+ if (gid_errno)
+ {
+ reason = g_strdup_printf ("couldn't set gid to %s (%ld)",
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) gid);
+ if (gid_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = gid_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+
+ if (uid_errno)
+ {
+ reason = g_strdup_printf ("couldn't set uid to %s (%ld)",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (long) uid);
+ if (uid_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = uid_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
+/* If we've been run as setuid or setgid to someone else (most likely root)
+ turn off the extra permissions so that random user-specified programs
+ don't get special privileges. (On some systems it is necessary to install
+ this program as setuid root in order to read the passwd file to implement
+ lock-mode.)
+
+ *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE!
+ If you do so, you will open a security hole. See the sections
+ of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
+ and "USING XDM".
+*/
+
+/* Returns TRUE if OK to lock, FALSE otherwise */
+gboolean
+hack_uid (char **nolock_reason,
+ char **orig_uid,
+ char **uid_message)
+{
+ char *reason;
+ gboolean ret;
+
+ ret = TRUE;
+ reason = NULL;
+
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = NULL;
+ }
+ if (orig_uid != NULL)
+ {
+ *orig_uid = NULL;
+ }
+ if (uid_message != NULL)
+ {
+ *uid_message = NULL;
+ }
+
+ /* Discard privileges, and set the effective user/group ids to the
+ real user/group ids. That is, give up our "chmod +s" rights.
+ */
+ {
+ uid_t euid = geteuid ();
+ gid_t egid = getegid ();
+ uid_t uid = getuid ();
+ gid_t gid = getgid ();
+
+ if (orig_uid != NULL)
+ {
+ *orig_uid = uid_gid_string (euid, egid);
+ }
+
+ if (uid != euid || gid != egid)
+ {
+ if (! set_ids_by_number (uid, gid, uid_message))
+ {
+ reason = g_strdup ("unable to discard privileges.");
+
+ ret = FALSE;
+ goto out;
+ }
+ }
+ }
+
+
+ /* Locking can't work when running as root, because we have no way of
+ knowing what the user id of the logged in user is (so we don't know
+ whose password to prompt for.)
+
+ *** WARNING: DO NOT DISABLE THIS CODE!
+ If you do so, you will open a security hole. See the sections
+ of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
+ and "USING XDM".
+ */
+ if (getuid () == (uid_t) 0)
+ {
+ reason = g_strdup ("running as root");
+ ret = FALSE;
+ goto out;
+ }
+
+out:
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup (reason);
+ }
+ g_free (reason);
+
+ return ret;
+}
diff --git a/src/setuid.c.orig b/src/setuid.c.orig
new file mode 100644
index 0000000..2c55c2e
--- /dev/null
+++ b/src/setuid.c.orig
@@ -0,0 +1,247 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * setuid.c --- management of runtime privileges.
+ *
+ * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "config.h"
+
+#ifndef EPERM
+#include <errno.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h> /* for getpwnam() and struct passwd */
+#include <grp.h> /* for getgrgid() and struct group */
+#include <glib.h>
+
+#include "setuid.h"
+
+static char *
+uid_gid_string (uid_t uid,
+ gid_t gid)
+{
+ static char *buf;
+ struct passwd *p = NULL;
+ struct group *g = NULL;
+
+ p = getpwuid (uid);
+ g = getgrgid (gid);
+
+ buf = g_strdup_printf ("%s/%s (%ld/%ld)",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) uid, (long) gid);
+
+ return buf;
+}
+
+static gboolean
+set_ids_by_number (uid_t uid,
+ gid_t gid,
+ char **message_ret)
+{
+ int uid_errno = 0;
+ int gid_errno = 0;
+ int sgs_errno = 0;
+ struct passwd *p = getpwuid (uid);
+ struct group *g = getgrgid (gid);
+
+ if (message_ret)
+ *message_ret = NULL;
+
+ /* Rumor has it that some implementations of of setuid() do nothing
+ when called with -1; therefore, if the "nobody" user has a uid of
+ -1, then that would be Really Bad. Rumor further has it that such
+ systems really ought to be using -2 for "nobody", since that works.
+ So, if we get a uid (or gid, for good measure) of -1, switch to -2
+ instead. Note that this must be done after we've looked up the
+ user/group names with getpwuid(-1) and/or getgrgid(-1).
+ */
+ if (gid == (gid_t) -1) gid = (gid_t) -2;
+ if (uid == (uid_t) -1) uid = (uid_t) -2;
+
+ errno = 0;
+ if (setgroups (1, &gid) < 0)
+ sgs_errno = errno ? errno : -1;
+
+ errno = 0;
+ if (setgid (gid) != 0)
+ gid_errno = errno ? errno : -1;
+
+ errno = 0;
+ if (setuid (uid) != 0)
+ uid_errno = errno ? errno : -1;
+
+ if (uid_errno == 0 && gid_errno == 0 && sgs_errno == 0)
+ {
+ static char *reason;
+ reason = g_strdup_printf ("changed uid/gid to %s/%s (%ld/%ld).",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) uid, (long) gid);
+ if (message_ret)
+ *message_ret = g_strdup (reason);
+
+ g_free (reason);
+
+ return TRUE;
+ }
+ else
+ {
+ char *reason = NULL;
+
+ if (sgs_errno)
+ {
+ reason = g_strdup_printf ("couldn't setgroups to %s (%ld)",
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) gid);
+ if (sgs_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = sgs_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+
+ if (gid_errno)
+ {
+ reason = g_strdup_printf ("couldn't set gid to %s (%ld)",
+ (g && g->gr_name ? g->gr_name : "???"),
+ (long) gid);
+ if (gid_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = gid_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+
+ if (uid_errno)
+ {
+ reason = g_strdup_printf ("couldn't set uid to %s (%ld)",
+ (p && p->pw_name ? p->pw_name : "???"),
+ (long) uid);
+ if (uid_errno == -1)
+ fprintf (stderr, "%s: unknown error\n", reason);
+ else
+ {
+ errno = uid_errno;
+ perror (reason);
+ }
+ g_free (reason);
+ reason = NULL;
+ }
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
+/* If we've been run as setuid or setgid to someone else (most likely root)
+ turn off the extra permissions so that random user-specified programs
+ don't get special privileges. (On some systems it is necessary to install
+ this program as setuid root in order to read the passwd file to implement
+ lock-mode.)
+
+ *** WARNING: DO NOT DISABLE ANY OF THE FOLLOWING CODE!
+ If you do so, you will open a security hole. See the sections
+ of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
+ and "USING XDM".
+*/
+
+/* Returns TRUE if OK to lock, FALSE otherwise */
+gboolean
+hack_uid (char **nolock_reason,
+ char **orig_uid,
+ char **uid_message)
+{
+ char *reason;
+ gboolean ret;
+
+ ret = TRUE;
+ reason = NULL;
+
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = NULL;
+ }
+ if (orig_uid != NULL)
+ {
+ *orig_uid = NULL;
+ }
+ if (uid_message != NULL)
+ {
+ *uid_message = NULL;
+ }
+
+ /* Discard privileges, and set the effective user/group ids to the
+ real user/group ids. That is, give up our "chmod +s" rights.
+ */
+ {
+ uid_t euid = geteuid ();
+ gid_t egid = getegid ();
+ uid_t uid = getuid ();
+ gid_t gid = getgid ();
+
+ if (orig_uid != NULL)
+ {
+ *orig_uid = uid_gid_string (euid, egid);
+ }
+
+ if (uid != euid || gid != egid)
+ {
+ if (! set_ids_by_number (uid, gid, uid_message))
+ {
+ reason = g_strdup ("unable to discard privileges.");
+
+ ret = FALSE;
+ goto out;
+ }
+ }
+ }
+
+
+ /* Locking can't work when running as root, because we have no way of
+ knowing what the user id of the logged in user is (so we don't know
+ whose password to prompt for.)
+
+ *** WARNING: DO NOT DISABLE THIS CODE!
+ If you do so, you will open a security hole. See the sections
+ of the xscreensaver manual titled "LOCKING AND ROOT LOGINS",
+ and "USING XDM".
+ */
+ if (getuid () == (uid_t) 0)
+ {
+ reason = g_strdup ("running as root");
+ ret = FALSE;
+ goto out;
+ }
+
+out:
+ if (nolock_reason != NULL)
+ {
+ *nolock_reason = g_strdup (reason);
+ }
+ g_free (reason);
+
+ return ret;
+}
diff --git a/src/setuid.h b/src/setuid.h
new file mode 100644
index 0000000..ffb490d
--- /dev/null
+++ b/src/setuid.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __GS_SETUID_H
+#define __GS_SETUID_H
+
+G_BEGIN_DECLS
+
+gboolean hack_uid (char **nolock_reason,
+ char **orig_uid,
+ char **uid_message);
+
+G_END_DECLS
+
+#endif /* __GS_SETUID_H */
diff --git a/src/setuid.h.orig b/src/setuid.h.orig
new file mode 100644
index 0000000..8cb8808
--- /dev/null
+++ b/src/setuid.h.orig
@@ -0,0 +1,25 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __GS_SETUID_H
+#define __GS_SETUID_H
+
+G_BEGIN_DECLS
+
+gboolean hack_uid (char **nolock_reason,
+ char **orig_uid,
+ char **uid_message);
+
+G_END_DECLS
+
+#endif /* __GS_SETUID_H */
diff --git a/src/subprocs.c b/src/subprocs.c
new file mode 100644
index 0000000..757daf8
--- /dev/null
+++ b/src/subprocs.c
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * subprocs.c --- choosing, spawning, and killing screenhacks.
+ *
+ * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski <[email protected]>
+ * Modified: Copyright (c) 2004 William Jon McCann <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef ESRCH
+# include <errno.h>
+#endif
+
+#include <sys/time.h> /* sys/resource.h needs this for timeval */
+# include <sys/wait.h> /* for waitpid() and associated macros */
+
+#ifdef VMS
+# include <processes.h>
+# include <unixio.h> /* for close */
+# include <unixlib.h> /* for getpid */
+# define pid_t int
+# define fork vfork
+#endif /* VMS */
+
+#include <signal.h> /* for the signal names */
+
+#include <glib.h>
+#include "subprocs.h"
+
+#if !defined(SIGCHLD) && defined(SIGCLD)
+# define SIGCHLD SIGCLD
+#endif
+
+/* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
+ Don't alter this directly -- use block_sigchld() / unblock_sigchld().
+*/
+static int block_sigchld_handler = 0;
+
+
+#ifdef HAVE_SIGACTION
+sigset_t
+#else /* !HAVE_SIGACTION */
+int
+#endif /* !HAVE_SIGACTION */
+block_sigchld (void)
+{
+#ifdef HAVE_SIGACTION
+ sigset_t child_set;
+ sigemptyset (&child_set);
+ sigaddset (&child_set, SIGCHLD);
+ sigaddset (&child_set, SIGPIPE);
+ sigprocmask (SIG_BLOCK, &child_set, 0);
+#endif /* HAVE_SIGACTION */
+
+ block_sigchld_handler++;
+
+#ifdef HAVE_SIGACTION
+ return child_set;
+#else /* !HAVE_SIGACTION */
+ return 0;
+#endif /* !HAVE_SIGACTION */
+}
+
+void
+unblock_sigchld (void)
+{
+#ifdef HAVE_SIGACTION
+ sigset_t child_set;
+ sigemptyset (&child_set);
+ sigaddset (&child_set, SIGCHLD);
+ sigaddset (&child_set, SIGPIPE);
+ sigprocmask (SIG_UNBLOCK, &child_set, 0);
+#endif /* HAVE_SIGACTION */
+
+ block_sigchld_handler--;
+}
+
+int
+signal_pid (int pid,
+ int signal)
+{
+ int status = -1;
+ gboolean verbose = TRUE;
+
+ if (block_sigchld_handler)
+ /* This function should not be called from the signal handler. */
+ abort();
+
+ block_sigchld (); /* we control the horizontal... */
+
+ status = kill (pid, signal);
+
+ if (verbose && status < 0)
+ {
+ if (errno == ESRCH)
+ g_message ("Child process %lu was already dead.",
+ (unsigned long) pid);
+ else
+ {
+ char buf [1024];
+ snprintf (buf, sizeof (buf), "Couldn't kill child process %lu",
+ (unsigned long) pid);
+ perror (buf);
+ }
+ }
+
+ unblock_sigchld ();
+
+ if (block_sigchld_handler < 0)
+ abort ();
+
+ return status;
+}
+
+#ifndef VMS
+
+void
+await_dying_children (int pid,
+ gboolean debug)
+{
+ while (1)
+ {
+ int wait_status = 0;
+ pid_t kid;
+
+ errno = 0;
+ kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
+
+ if (debug)
+ {
+ if (kid < 0 && errno)
+ g_message ("waitpid(%d) ==> %ld (%d)", pid, (long) kid, errno);
+ else if (kid != 0)
+ g_message ("waitpid(%d) ==> %ld", pid, (long) kid);
+ }
+
+ /* 0 means no more children to reap.
+ -1 means error -- except "interrupted system call" isn't a "real"
+ error, so if we get that, we should just try again. */
+ if (kid < 0 && errno != EINTR)
+ break;
+ }
+}
+
+
+#else /* VMS */
+static void await_dying_children (saver_info *si)
+{
+ return;
+}
+#endif /* VMS */
+
diff --git a/src/subprocs.c.orig b/src/subprocs.c.orig
new file mode 100644
index 0000000..3c18e08
--- /dev/null
+++ b/src/subprocs.c.orig
@@ -0,0 +1,166 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * subprocs.c --- choosing, spawning, and killing screenhacks.
+ *
+ * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski <[email protected]>
+ * Modified: Copyright (c) 2004 William Jon McCann <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifndef ESRCH
+# include <errno.h>
+#endif
+
+#include <sys/time.h> /* sys/resource.h needs this for timeval */
+# include <sys/wait.h> /* for waitpid() and associated macros */
+
+#ifdef VMS
+# include <processes.h>
+# include <unixio.h> /* for close */
+# include <unixlib.h> /* for getpid */
+# define pid_t int
+# define fork vfork
+#endif /* VMS */
+
+#include <signal.h> /* for the signal names */
+
+#include <glib.h>
+#include "subprocs.h"
+
+#if !defined(SIGCHLD) && defined(SIGCLD)
+# define SIGCHLD SIGCLD
+#endif
+
+/* Semaphore to temporarily turn the SIGCHLD handler into a no-op.
+ Don't alter this directly -- use block_sigchld() / unblock_sigchld().
+*/
+static int block_sigchld_handler = 0;
+
+
+#ifdef HAVE_SIGACTION
+sigset_t
+#else /* !HAVE_SIGACTION */
+int
+#endif /* !HAVE_SIGACTION */
+block_sigchld (void)
+{
+#ifdef HAVE_SIGACTION
+ sigset_t child_set;
+ sigemptyset (&child_set);
+ sigaddset (&child_set, SIGCHLD);
+ sigaddset (&child_set, SIGPIPE);
+ sigprocmask (SIG_BLOCK, &child_set, 0);
+#endif /* HAVE_SIGACTION */
+
+ block_sigchld_handler++;
+
+#ifdef HAVE_SIGACTION
+ return child_set;
+#else /* !HAVE_SIGACTION */
+ return 0;
+#endif /* !HAVE_SIGACTION */
+}
+
+void
+unblock_sigchld (void)
+{
+#ifdef HAVE_SIGACTION
+ sigset_t child_set;
+ sigemptyset (&child_set);
+ sigaddset (&child_set, SIGCHLD);
+ sigaddset (&child_set, SIGPIPE);
+ sigprocmask (SIG_UNBLOCK, &child_set, 0);
+#endif /* HAVE_SIGACTION */
+
+ block_sigchld_handler--;
+}
+
+int
+signal_pid (int pid,
+ int signal)
+{
+ int status = -1;
+ gboolean verbose = TRUE;
+
+ if (block_sigchld_handler)
+ /* This function should not be called from the signal handler. */
+ abort();
+
+ block_sigchld (); /* we control the horizontal... */
+
+ status = kill (pid, signal);
+
+ if (verbose && status < 0)
+ {
+ if (errno == ESRCH)
+ g_message ("Child process %lu was already dead.",
+ (unsigned long) pid);
+ else
+ {
+ char buf [1024];
+ snprintf (buf, sizeof (buf), "Couldn't kill child process %lu",
+ (unsigned long) pid);
+ perror (buf);
+ }
+ }
+
+ unblock_sigchld ();
+
+ if (block_sigchld_handler < 0)
+ abort ();
+
+ return status;
+}
+
+#ifndef VMS
+
+void
+await_dying_children (int pid,
+ gboolean debug)
+{
+ while (1)
+ {
+ int wait_status = 0;
+ pid_t kid;
+
+ errno = 0;
+ kid = waitpid (-1, &wait_status, WNOHANG|WUNTRACED);
+
+ if (debug)
+ {
+ if (kid < 0 && errno)
+ g_message ("waitpid(%d) ==> %ld (%d)", pid, (long) kid, errno);
+ else if (kid != 0)
+ g_message ("waitpid(%d) ==> %ld", pid, (long) kid);
+ }
+
+ /* 0 means no more children to reap.
+ -1 means error -- except "interrupted system call" isn't a "real"
+ error, so if we get that, we should just try again. */
+ if (kid < 0 && errno != EINTR)
+ break;
+ }
+}
+
+
+#else /* VMS */
+static void await_dying_children (saver_info *si)
+{
+ return;
+}
+#endif /* VMS */
+
diff --git a/src/subprocs.h b/src/subprocs.h
new file mode 100644
index 0000000..95744cd
--- /dev/null
+++ b/src/subprocs.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * subprocs.c --- choosing, spawning, and killing screenhacks.
+ *
+ * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __GS_SUBPROCS_H
+#define __GS_SUBPROCS_H
+
+#include <signal.h>
+
+G_BEGIN_DECLS
+
+void unblock_sigchld (void);
+
+#ifdef HAVE_SIGACTION
+sigset_t
+#else /* !HAVE_SIGACTION */
+int
+#endif /* !HAVE_SIGACTION */
+block_sigchld (void);
+
+int signal_pid (int pid,
+ int signal);
+void await_dying_children (int pid,
+ gboolean debug);
+
+G_END_DECLS
+
+#endif /* __GS_SUBPROCS_H */
diff --git a/src/subprocs.h.orig b/src/subprocs.h.orig
new file mode 100644
index 0000000..30fec7b
--- /dev/null
+++ b/src/subprocs.h.orig
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * subprocs.c --- choosing, spawning, and killing screenhacks.
+ *
+ * xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski <[email protected]>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ */
+
+#ifndef __GS_SUBPROCS_H
+#define __GS_SUBPROCS_H
+
+#include <signal.h>
+
+G_BEGIN_DECLS
+
+void unblock_sigchld (void);
+
+#ifdef HAVE_SIGACTION
+sigset_t
+#else /* !HAVE_SIGACTION */
+int
+#endif /* !HAVE_SIGACTION */
+block_sigchld (void);
+
+int signal_pid (int pid,
+ int signal);
+void await_dying_children (int pid,
+ gboolean debug);
+
+G_END_DECLS
+
+#endif /* __GS_SUBPROCS_H */
diff --git a/src/test-fade.c b/src/test-fade.c
new file mode 100644
index 0000000..2bc6c92
--- /dev/null
+++ b/src/test-fade.c
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-fade.h"
+#include "gs-debug.h"
+
+#ifdef HAVE_XF86VMODE_GAMMA
+# include <X11/extensions/xf86vmode.h>
+#endif
+
+#define XF86_VIDMODE_NAME "XFree86-VidModeExtension"
+
+static void
+test_fade (void)
+{
+ GSFade *fade;
+ int reps = 2;
+ int delay = 2;
+
+ fade = gs_fade_new ();
+
+ while (reps-- > 0)
+ {
+
+ g_print ("fading out...");
+ gs_fade_sync (fade, 1000);
+ g_print ("done.\n");
+
+ g_print ("fading in...");
+ gs_fade_reset (fade);
+ g_print ("done.\n");
+
+ if (delay)
+ {
+ sleep (delay);
+ }
+ }
+
+ g_object_unref (fade);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+ int op, event, err;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (error)
+ {
+ fprintf (stderr, "%s\n", error->message);
+ exit (1);
+ }
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (! XQueryExtension (GDK_DISPLAY (), XF86_VIDMODE_NAME, &op, &event, &err))
+ {
+ g_message ("no " XF86_VIDMODE_NAME " extension");
+ }
+ else
+ {
+# ifdef HAVE_XF86VMODE_GAMMA
+ int major;
+ int minor;
+
+ if (! XF86VidModeQueryVersion (GDK_DISPLAY (), &major, &minor))
+ {
+ g_message ("unable to get " XF86_VIDMODE_NAME " version");
+ }
+ else
+ {
+ g_message (XF86_VIDMODE_NAME " version %d.%d", major, minor);
+ }
+# else /* !HAVE_XF86VMODE_GAMMA */
+ g_message ("no support for display's " XF86_VIDMODE_NAME " extension");
+# endif /* !HAVE_XF86VMODE_GAMMA */
+ }
+
+ gs_debug_init (TRUE, FALSE);
+
+ test_fade ();
+
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/test-fade.c.orig b/src/test-fade.c.orig
new file mode 100644
index 0000000..894085c
--- /dev/null
+++ b/src/test-fade.c.orig
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include "gs-fade.h"
+#include "gs-debug.h"
+
+#ifdef HAVE_XF86VMODE_GAMMA
+# include <X11/extensions/xf86vmode.h>
+#endif
+
+#define XF86_VIDMODE_NAME "XFree86-VidModeExtension"
+
+static void
+test_fade (void)
+{
+ GSFade *fade;
+ int reps = 2;
+ int delay = 2;
+
+ fade = gs_fade_new ();
+
+ while (reps-- > 0)
+ {
+
+ g_print ("fading out...");
+ gs_fade_sync (fade, 1000);
+ g_print ("done.\n");
+
+ g_print ("fading in...");
+ gs_fade_reset (fade);
+ g_print ("done.\n");
+
+ if (delay)
+ {
+ sleep (delay);
+ }
+ }
+
+ g_object_unref (fade);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+ int op, event, err;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (error)
+ {
+ fprintf (stderr, "%s\n", error->message);
+ exit (1);
+ }
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (! XQueryExtension (GDK_DISPLAY (), XF86_VIDMODE_NAME, &op, &event, &err))
+ {
+ g_message ("no " XF86_VIDMODE_NAME " extension");
+ }
+ else
+ {
+# ifdef HAVE_XF86VMODE_GAMMA
+ int major;
+ int minor;
+
+ if (! XF86VidModeQueryVersion (GDK_DISPLAY (), &major, &minor))
+ {
+ g_message ("unable to get " XF86_VIDMODE_NAME " version");
+ }
+ else
+ {
+ g_message (XF86_VIDMODE_NAME " version %d.%d", major, minor);
+ }
+# else /* !HAVE_XF86VMODE_GAMMA */
+ g_message ("no support for display's " XF86_VIDMODE_NAME " extension");
+# endif /* !HAVE_XF86VMODE_GAMMA */
+ }
+
+ gs_debug_init (TRUE, FALSE);
+
+ test_fade ();
+
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/test-passwd.c b/src/test-passwd.c
new file mode 100644
index 0000000..3f7a849
--- /dev/null
+++ b/src/test-passwd.c
@@ -0,0 +1,288 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-auth.h"
+#include "setuid.h"
+
+/* Initializations that potentially take place as a priveleged user:
+ If the executable is setuid root, then these initializations
+ are run as root, before discarding privileges.
+*/
+static gboolean
+privileged_initialization (void)
+{
+ gboolean ret;
+ char *nolock_reason;
+ char *orig_uid;
+ char *uid_message;
+
+#ifndef NO_LOCKING
+ /* before hack_uid () for proper permissions */
+ gs_auth_priv_init ();
+#endif /* NO_LOCKING */
+
+ ret = hack_uid (&nolock_reason,
+ &orig_uid,
+ &uid_message);
+ if (nolock_reason)
+ {
+ g_warning ("Locking disabled: %s", nolock_reason);
+ }
+ if (uid_message && gs_auth_get_verbose ())
+ {
+ g_print ("Modified UID: %s", uid_message);
+ }
+
+ g_free (nolock_reason);
+ g_free (orig_uid);
+ g_free (uid_message);
+
+ return ret;
+}
+
+
+/* Figure out what locking mechanisms are supported.
+ */
+static gboolean
+lock_initialization (char **nolock_reason)
+{
+ if (nolock_reason)
+ {
+ *nolock_reason = NULL;
+ }
+
+#ifdef NO_LOCKING
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("not compiled with locking support");
+ }
+ return FALSE;
+#else /* !NO_LOCKING */
+
+ /* Finish initializing locking, now that we're out of privileged code. */
+ if (! gs_auth_init ())
+ {
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("error getting password");
+ }
+ return FALSE;
+ }
+
+ /* If locking is currently enabled, but the environment indicates that
+ we have been launched as MDM's "Background" program, then disable
+ locking just in case.
+ */
+ if (getenv ("RUNNING_UNDER_MDM"))
+ {
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("running under MDM");
+ }
+ return FALSE;
+ }
+
+ /* If the server is XDarwin (MacOS X) then disable locking.
+ (X grabs only affect X programs, so you can use Command-Tab
+ to bring any other Mac program to the front, e.g., Terminal.)
+ */
+ {
+ gboolean macos = FALSE;
+
+#ifdef __APPLE__
+ /* Disable locking if *running* on Apple hardware, since we have no
+ reliable way to determine whether the server is running on MacOS.
+ Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
+ but I'm not really sure about that.
+ */
+ macos = TRUE;
+#endif
+
+ if (macos)
+ {
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("Cannot lock securely on MacOS X");
+ }
+ return FALSE;
+ }
+ }
+
+#endif /* NO_LOCKING */
+
+ return TRUE;
+}
+
+static char *
+request_password (const char *prompt)
+{
+ char buf [255];
+ char *pass;
+ char *password;
+ struct termios ts0;
+ struct termios ts1;
+
+ tcgetattr (fileno (stdin), &ts0);
+ ts1 = ts0;
+ ts1.c_lflag &= ~ECHO;
+
+ printf ("%s", prompt);
+
+ if (tcsetattr (fileno (stdin), TCSAFLUSH, &ts1) != 0)
+ {
+ fprintf (stderr, "Could not set terminal attributes\n");
+ exit (1);
+ }
+
+ pass = fgets (buf, sizeof (buf) - 1, stdin);
+
+ tcsetattr (fileno (stdin), TCSANOW, &ts0);
+
+ if (!pass || !*pass)
+ {
+ exit (0);
+ }
+
+ if (pass [strlen (pass) - 1] == '\n')
+ {
+ pass [strlen (pass) - 1] = 0;
+ }
+
+ password = g_strdup (pass);
+
+ memset (pass, '\b', strlen (pass));
+
+ return password;
+}
+
+static gboolean
+auth_message_handler (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data)
+{
+ gboolean ret;
+
+ g_message ("Got message style %d: '%s'", style, msg);
+
+ ret = TRUE;
+
+ switch (style)
+ {
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
+ break;
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
+ {
+ char *password;
+ password = request_password (msg);
+ *response = password;
+ }
+ break;
+ case GS_AUTH_MESSAGE_ERROR_MSG:
+ break;
+ case GS_AUTH_MESSAGE_TEXT_INFO:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return ret;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+ gboolean verbose = TRUE;
+ char *nolock_reason = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! g_thread_supported ())
+ {
+ g_thread_init (NULL);
+ }
+
+ gs_auth_set_verbose (verbose);
+ if (! privileged_initialization ())
+ {
+ exit (1);
+ }
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (! lock_initialization (&nolock_reason))
+ {
+ if (nolock_reason)
+ {
+ g_warning ("Screen locking disabled: %s", nolock_reason);
+ g_free (nolock_reason);
+ }
+
+ exit (1);
+ }
+
+again:
+ error = NULL;
+
+ if (gs_auth_verify_user (g_get_user_name (), g_getenv ("DISPLAY"), auth_message_handler, NULL, &error))
+ {
+ printf ("Correct!\n");
+ }
+ else
+ {
+ if (error != NULL)
+ {
+ fprintf (stderr, "ERROR: %s\n", error->message);
+ g_error_free (error);
+ }
+ printf ("Incorrect\n");
+ goto again;
+ }
+
+ return 0;
+}
diff --git a/src/test-passwd.c.orig b/src/test-passwd.c.orig
new file mode 100644
index 0000000..70bccce
--- /dev/null
+++ b/src/test-passwd.c.orig
@@ -0,0 +1,288 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005-2006 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-auth.h"
+#include "setuid.h"
+
+/* Initializations that potentially take place as a priveleged user:
+ If the executable is setuid root, then these initializations
+ are run as root, before discarding privileges.
+*/
+static gboolean
+privileged_initialization (void)
+{
+ gboolean ret;
+ char *nolock_reason;
+ char *orig_uid;
+ char *uid_message;
+
+#ifndef NO_LOCKING
+ /* before hack_uid () for proper permissions */
+ gs_auth_priv_init ();
+#endif /* NO_LOCKING */
+
+ ret = hack_uid (&nolock_reason,
+ &orig_uid,
+ &uid_message);
+ if (nolock_reason)
+ {
+ g_warning ("Locking disabled: %s", nolock_reason);
+ }
+ if (uid_message && gs_auth_get_verbose ())
+ {
+ g_print ("Modified UID: %s", uid_message);
+ }
+
+ g_free (nolock_reason);
+ g_free (orig_uid);
+ g_free (uid_message);
+
+ return ret;
+}
+
+
+/* Figure out what locking mechanisms are supported.
+ */
+static gboolean
+lock_initialization (char **nolock_reason)
+{
+ if (nolock_reason)
+ {
+ *nolock_reason = NULL;
+ }
+
+#ifdef NO_LOCKING
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("not compiled with locking support");
+ }
+ return FALSE;
+#else /* !NO_LOCKING */
+
+ /* Finish initializing locking, now that we're out of privileged code. */
+ if (! gs_auth_init ())
+ {
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("error getting password");
+ }
+ return FALSE;
+ }
+
+ /* If locking is currently enabled, but the environment indicates that
+ we have been launched as MDM's "Background" program, then disable
+ locking just in case.
+ */
+ if (getenv ("RUNNING_UNDER_MDM"))
+ {
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("running under MDM");
+ }
+ return FALSE;
+ }
+
+ /* If the server is XDarwin (MacOS X) then disable locking.
+ (X grabs only affect X programs, so you can use Command-Tab
+ to bring any other Mac program to the front, e.g., Terminal.)
+ */
+ {
+ gboolean macos = FALSE;
+
+#ifdef __APPLE__
+ /* Disable locking if *running* on Apple hardware, since we have no
+ reliable way to determine whether the server is running on MacOS.
+ Hopefully __APPLE__ means "MacOS" and not "Linux on Mac hardware"
+ but I'm not really sure about that.
+ */
+ macos = TRUE;
+#endif
+
+ if (macos)
+ {
+ if (nolock_reason)
+ {
+ *nolock_reason = g_strdup ("Cannot lock securely on MacOS X");
+ }
+ return FALSE;
+ }
+ }
+
+#endif /* NO_LOCKING */
+
+ return TRUE;
+}
+
+static char *
+request_password (const char *prompt)
+{
+ char buf [255];
+ char *pass;
+ char *password;
+ struct termios ts0;
+ struct termios ts1;
+
+ tcgetattr (fileno (stdin), &ts0);
+ ts1 = ts0;
+ ts1.c_lflag &= ~ECHO;
+
+ printf ("%s", prompt);
+
+ if (tcsetattr (fileno (stdin), TCSAFLUSH, &ts1) != 0)
+ {
+ fprintf (stderr, "Could not set terminal attributes\n");
+ exit (1);
+ }
+
+ pass = fgets (buf, sizeof (buf) - 1, stdin);
+
+ tcsetattr (fileno (stdin), TCSANOW, &ts0);
+
+ if (!pass || !*pass)
+ {
+ exit (0);
+ }
+
+ if (pass [strlen (pass) - 1] == '\n')
+ {
+ pass [strlen (pass) - 1] = 0;
+ }
+
+ password = g_strdup (pass);
+
+ memset (pass, '\b', strlen (pass));
+
+ return password;
+}
+
+static gboolean
+auth_message_handler (GSAuthMessageStyle style,
+ const char *msg,
+ char **response,
+ gpointer data)
+{
+ gboolean ret;
+
+ g_message ("Got message style %d: '%s'", style, msg);
+
+ ret = TRUE;
+
+ switch (style)
+ {
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_ON:
+ break;
+ case GS_AUTH_MESSAGE_PROMPT_ECHO_OFF:
+ {
+ char *password;
+ password = request_password (msg);
+ *response = password;
+ }
+ break;
+ case GS_AUTH_MESSAGE_ERROR_MSG:
+ break;
+ case GS_AUTH_MESSAGE_TEXT_INFO:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return ret;
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+ gboolean verbose = TRUE;
+ char *nolock_reason = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! g_thread_supported ())
+ {
+ g_thread_init (NULL);
+ }
+
+ gs_auth_set_verbose (verbose);
+ if (! privileged_initialization ())
+ {
+ exit (1);
+ }
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ if (! lock_initialization (&nolock_reason))
+ {
+ if (nolock_reason)
+ {
+ g_warning ("Screen locking disabled: %s", nolock_reason);
+ g_free (nolock_reason);
+ }
+
+ exit (1);
+ }
+
+again:
+ error = NULL;
+
+ if (gs_auth_verify_user (g_get_user_name (), g_getenv ("DISPLAY"), auth_message_handler, NULL, &error))
+ {
+ printf ("Correct!\n");
+ }
+ else
+ {
+ if (error != NULL)
+ {
+ fprintf (stderr, "ERROR: %s\n", error->message);
+ g_error_free (error);
+ }
+ printf ("Incorrect\n");
+ goto again;
+ }
+
+ return 0;
+}
diff --git a/src/test-watcher.c b/src/test-watcher.c
new file mode 100644
index 0000000..5e8ca08
--- /dev/null
+++ b/src/test-watcher.c
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-watcher.h"
+#include "gs-debug.h"
+
+static gboolean
+watcher_idle_cb (GSWatcher *watcher,
+ gboolean is_idle,
+ gpointer data)
+{
+ g_message ("Idle status changed: %s", is_idle ? "idle" : "not idle");
+
+ /* return FALSE so that the idle watcher continues */
+ return FALSE;
+}
+
+static gboolean
+watcher_idle_notice_cb (GSWatcher *watcher,
+ gboolean is_idle,
+ gpointer data)
+{
+ g_message ("Idle notice status changed: %s", is_idle ? "idle" : "not idle");
+
+ return TRUE;
+}
+
+static void
+connect_watcher_signals (GSWatcher *watcher)
+{
+ g_signal_connect (watcher, "idle-changed",
+ G_CALLBACK (watcher_idle_cb), NULL);
+ g_signal_connect (watcher, "idle-notice-changed",
+ G_CALLBACK (watcher_idle_notice_cb), NULL);
+}
+
+static void
+test_watcher (void)
+{
+ GSWatcher *watcher;
+
+ watcher = gs_watcher_new ();
+ gs_watcher_set_enabled (watcher, TRUE);
+ gs_watcher_set_active (watcher, TRUE);
+
+ connect_watcher_signals (watcher);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ gs_debug_init (TRUE, FALSE);
+
+ test_watcher ();
+
+ gtk_main ();
+
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/test-watcher.c.orig b/src/test-watcher.c.orig
new file mode 100644
index 0000000..28c7678
--- /dev/null
+++ b/src/test-watcher.c.orig
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-watcher.h"
+#include "gs-debug.h"
+
+static gboolean
+watcher_idle_cb (GSWatcher *watcher,
+ gboolean is_idle,
+ gpointer data)
+{
+ g_message ("Idle status changed: %s", is_idle ? "idle" : "not idle");
+
+ /* return FALSE so that the idle watcher continues */
+ return FALSE;
+}
+
+static gboolean
+watcher_idle_notice_cb (GSWatcher *watcher,
+ gboolean is_idle,
+ gpointer data)
+{
+ g_message ("Idle notice status changed: %s", is_idle ? "idle" : "not idle");
+
+ return TRUE;
+}
+
+static void
+connect_watcher_signals (GSWatcher *watcher)
+{
+ g_signal_connect (watcher, "idle-changed",
+ G_CALLBACK (watcher_idle_cb), NULL);
+ g_signal_connect (watcher, "idle-notice-changed",
+ G_CALLBACK (watcher_idle_notice_cb), NULL);
+}
+
+static void
+test_watcher (void)
+{
+ GSWatcher *watcher;
+
+ watcher = gs_watcher_new ();
+ gs_watcher_set_enabled (watcher, TRUE);
+ gs_watcher_set_active (watcher, TRUE);
+
+ connect_watcher_signals (watcher);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ gs_debug_init (TRUE, FALSE);
+
+ test_watcher ();
+
+ gtk_main ();
+
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/test-window.c b/src/test-window.c
new file mode 100644
index 0000000..6600411
--- /dev/null
+++ b/src/test-window.c
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-window.h"
+#include "gs-grab.h"
+#include "gs-debug.h"
+
+static GSGrab *grab = NULL;
+
+static void
+window_deactivated_cb (GSWindow *window,
+ gpointer data)
+{
+ gs_window_destroy (window);
+}
+
+static void
+window_dialog_up_cb (GSWindow *window,
+ gpointer data)
+{
+}
+
+static void
+window_dialog_down_cb (GSWindow *window,
+ gpointer data)
+{
+}
+
+static void
+window_show_cb (GSWindow *window,
+ gpointer data)
+{
+ /* Grab keyboard so dialog can be used */
+ gs_grab_move_to_window (grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+
+}
+
+static gboolean
+window_activity_cb (GSWindow *window,
+ gpointer data)
+{
+ gs_window_request_unlock (window);
+
+ return TRUE;
+}
+
+static void
+disconnect_window_signals (GSWindow *window)
+{
+ gpointer data;
+
+ data = NULL;
+ g_signal_handlers_disconnect_by_func (window, window_activity_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_deactivated_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_dialog_up_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_dialog_down_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_show_cb, data);
+}
+
+static void
+window_destroyed_cb (GtkWindow *window,
+ gpointer data)
+{
+ disconnect_window_signals (GS_WINDOW (window));
+ gs_grab_release (grab);
+ gtk_main_quit ();
+}
+
+static void
+connect_window_signals (GSWindow *window)
+{
+ gpointer data;
+
+ data = NULL;
+
+ g_signal_connect_object (window, "activity",
+ G_CALLBACK (window_activity_cb), data, 0);
+ g_signal_connect_object (window, "destroy",
+ G_CALLBACK (window_destroyed_cb), data, 0);
+ g_signal_connect_object (window, "deactivated",
+ G_CALLBACK (window_deactivated_cb), data, 0);
+ g_signal_connect_object (window, "dialog-up",
+ G_CALLBACK (window_dialog_up_cb), data, 0);
+ g_signal_connect_object (window, "dialog-down",
+ G_CALLBACK (window_dialog_down_cb), data, 0);
+ g_signal_connect_object (window, "show",
+ G_CALLBACK (window_show_cb), data, 0);
+}
+
+static void
+test_window (void)
+{
+ GSWindow *window;
+ gboolean lock_active;
+ gboolean user_switch_enabled;
+ GdkScreen *screen;
+ int monitor;
+
+ lock_active = TRUE;
+ user_switch_enabled = TRUE;
+ screen = gdk_screen_get_default ();
+ monitor = 0;
+
+ window = gs_window_new (screen, monitor, lock_active);
+
+ gs_window_set_user_switch_enabled (window, user_switch_enabled);
+
+ connect_window_signals (window);
+
+ gs_window_show (window);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ gs_debug_init (TRUE, FALSE);
+
+ grab = gs_grab_new ();
+
+ test_window ();
+
+ /* safety valve in case we can't authenticate */
+ g_timeout_add (30000, (GSourceFunc)gtk_main_quit, NULL);
+
+ gtk_main ();
+
+ g_object_unref (grab);
+
+ gs_debug_shutdown ();
+
+ return 0;
+}
diff --git a/src/test-window.c.orig b/src/test-window.c.orig
new file mode 100644
index 0000000..bc4d37b
--- /dev/null
+++ b/src/test-window.c.orig
@@ -0,0 +1,180 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2005 William Jon McCann <[email protected]>
+ *
+ * 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.
+ *
+ * Authors: William Jon McCann <[email protected]>
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "gs-window.h"
+#include "gs-grab.h"
+#include "gs-debug.h"
+
+static GSGrab *grab = NULL;
+
+static void
+window_deactivated_cb (GSWindow *window,
+ gpointer data)
+{
+ gs_window_destroy (window);
+}
+
+static void
+window_dialog_up_cb (GSWindow *window,
+ gpointer data)
+{
+}
+
+static void
+window_dialog_down_cb (GSWindow *window,
+ gpointer data)
+{
+}
+
+static void
+window_show_cb (GSWindow *window,
+ gpointer data)
+{
+ /* Grab keyboard so dialog can be used */
+ gs_grab_move_to_window (grab,
+ gs_window_get_gdk_window (window),
+ gs_window_get_screen (window),
+ FALSE);
+
+}
+
+static gboolean
+window_activity_cb (GSWindow *window,
+ gpointer data)
+{
+ gs_window_request_unlock (window);
+
+ return TRUE;
+}
+
+static void
+disconnect_window_signals (GSWindow *window)
+{
+ gpointer data;
+
+ data = NULL;
+ g_signal_handlers_disconnect_by_func (window, window_activity_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_deactivated_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_dialog_up_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_dialog_down_cb, data);
+ g_signal_handlers_disconnect_by_func (window, window_show_cb, data);
+}
+
+static void
+window_destroyed_cb (GtkWindow *window,
+ gpointer data)
+{
+ disconnect_window_signals (GS_WINDOW (window));
+ gs_grab_release (grab);
+ gtk_main_quit ();
+}
+
+static void
+connect_window_signals (GSWindow *window)
+{
+ gpointer data;
+
+ data = NULL;
+
+ g_signal_connect_object (window, "activity",
+ G_CALLBACK (window_activity_cb), data, 0);
+ g_signal_connect_object (window, "destroy",
+ G_CALLBACK (window_destroyed_cb), data, 0);
+ g_signal_connect_object (window, "deactivated",
+ G_CALLBACK (window_deactivated_cb), data, 0);
+ g_signal_connect_object (window, "dialog-up",
+ G_CALLBACK (window_dialog_up_cb), data, 0);
+ g_signal_connect_object (window, "dialog-down",
+ G_CALLBACK (window_dialog_down_cb), data, 0);
+ g_signal_connect_object (window, "show",
+ G_CALLBACK (window_show_cb), data, 0);
+}
+
+static void
+test_window (void)
+{
+ GSWindow *window;
+ gboolean lock_active;
+ gboolean user_switch_enabled;
+ GdkScreen *screen;
+ int monitor;
+
+ lock_active = TRUE;
+ user_switch_enabled = TRUE;
+ screen = gdk_screen_get_default ();
+ monitor = 0;
+
+ window = gs_window_new (screen, monitor, lock_active);
+
+ gs_window_set_user_switch_enabled (window, user_switch_enabled);
+
+ connect_window_signals (window);
+
+ gs_window_show (window);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ GError *error = NULL;
+
+#ifdef ENABLE_NLS
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+# endif
+ textdomain (GETTEXT_PACKAGE);
+#endif
+
+ if (! gtk_init_with_args (&argc, &argv, NULL, NULL, NULL, &error))
+ {
+ fprintf (stderr, "%s", error->message);
+ g_error_free (error);
+ exit (1);
+ }
+
+ gs_debug_init (TRUE, FALSE);
+
+ grab = gs_grab_new ();
+
+ test_window ();
+
+ /* safety valve in case we can't authenticate */
+ g_timeout_add (30000, (GSourceFunc)gtk_main_quit, NULL);
+
+ gtk_main ();
+
+ g_object_unref (grab);
+
+ gs_debug_shutdown ();
+
+ return 0;
+}