diff options
Diffstat (limited to 'src')
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", + ©_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", + ©_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 (), + ¤t_server_timeout, + ¤t_server_interval, + ¤t_prefer_blank, + ¤t_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 (), + ¤t_server_timeout, + ¤t_server_interval, + ¤t_prefer_blank, + ¤t_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; +} |