From 8d967c46fe8debcd3eedbb0bbf43a6545429b243 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 8 May 2015 16:24:59 -0400 Subject: [PATCH 01/17] Revert "Remove gnome-session-properties" This reverts commit ea285af9962313ee2675fff27d3a852bb61e936a. --- Makefile.am | 1 + capplet/Makefile.am | 31 + capplet/gsm-app-dialog.c | 540 +++++++++++++ capplet/gsm-app-dialog.h | 66 ++ capplet/gsm-properties-dialog.c | 774 +++++++++++++++++++ capplet/gsm-properties-dialog.h | 57 ++ capplet/gsp-app-manager.c | 593 ++++++++++++++ capplet/gsp-app-manager.h | 81 ++ capplet/gsp-app.c | 1102 +++++++++++++++++++++++++++ capplet/gsp-app.h | 108 +++ capplet/gsp-keyfile.c | 201 +++++ capplet/gsp-keyfile.h | 65 ++ capplet/main.c | 108 +++ configure.ac | 2 + data/Makefile.am | 6 + data/gnome-session-properties.desktop.in.in | 15 + doc/man/Makefile.am | 1 + doc/man/gnome-session-properties.1 | 24 + po/POTFILES.in | 5 + 19 files changed, 3780 insertions(+) create mode 100644 capplet/Makefile.am create mode 100644 capplet/gsm-app-dialog.c create mode 100644 capplet/gsm-app-dialog.h create mode 100644 capplet/gsm-properties-dialog.c create mode 100644 capplet/gsm-properties-dialog.h create mode 100644 capplet/gsp-app-manager.c create mode 100644 capplet/gsp-app-manager.h create mode 100644 capplet/gsp-app.c create mode 100644 capplet/gsp-app.h create mode 100644 capplet/gsp-keyfile.c create mode 100644 capplet/gsp-keyfile.h create mode 100644 capplet/main.c create mode 100644 data/gnome-session-properties.desktop.in.in create mode 100644 doc/man/gnome-session-properties.1 diff --git a/Makefile.am b/Makefile.am index 6560a4e..6b6a47b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,32 +1,33 @@ SUBDIRS = \ gnome-session \ + capplet \ tools \ data \ doc \ po ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} EXTRA_DIST = \ HACKING \ MAINTAINERS MAINTAINERCLEANFILES = \ $(srcdir)/INSTALL \ $(srcdir)/aclocal.m4 \ $(srcdir)/config.guess \ $(srcdir)/config.h.in \ $(srcdir)/config.sub \ $(srcdir)/depcomp \ $(srcdir)/install-sh \ $(srcdir)/ltmain.sh \ $(srcdir)/missing \ $(srcdir)/mkinstalldirs \ $(srcdir)/configure \ $(srcdir)/m4/intltool.m4 \ `find "$(srcdir)" -type f -name Makefile.in -print` CHANGELOG_GIT_RANGE = GNOME_SESSION_2_26_1.. dist-hook: $(AM_V_GEN)if test -d "$(srcdir)/.git"; then \ ( echo '# Generated by Makefile. Do not edit.'; echo; \ diff --git a/capplet/Makefile.am b/capplet/Makefile.am new file mode 100644 index 0000000..c2e563c --- /dev/null +++ b/capplet/Makefile.am @@ -0,0 +1,31 @@ +bin_PROGRAMS = gnome-session-properties + +AM_CPPFLAGS = \ + $(SESSION_PROPERTIES_CFLAGS) \ + $(GCONF_CFLAGS) \ + -I$(top_srcdir)/gnome-session \ + -DLOCALE_DIR=\""$(datadir)/locale"\" \ + -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \ + $(DISABLE_DEPRECATED_CFLAGS) + +AM_CFLAGS = $(WARN_CFLAGS) + +gnome_session_properties_SOURCES = \ + main.c \ + gsm-properties-dialog.h \ + gsm-properties-dialog.c \ + gsm-app-dialog.h \ + gsm-app-dialog.c \ + gsp-app.h \ + gsp-app.c \ + gsp-app-manager.h \ + gsp-app-manager.c \ + gsp-keyfile.h \ + gsp-keyfile.c + +gnome_session_properties_LDADD = \ + $(SESSION_PROPERTIES_LIBS) \ + $(top_builddir)/gnome-session/libgsmutil.la \ + $(GCONF_LIBS) + +-include $(top_srcdir)/git.mk diff --git a/capplet/gsm-app-dialog.c b/capplet/gsm-app-dialog.c new file mode 100644 index 0000000..e7369dd --- /dev/null +++ b/capplet/gsm-app-dialog.c @@ -0,0 +1,540 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * + * 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 +#include +#include + +#include "gsm-util.h" + +#include "gsm-app-dialog.h" + +#define GTKBUILDER_FILE "session-properties.ui" + +#define CAPPLET_NAME_ENTRY_WIDGET_NAME "session_properties_name_entry" +#define CAPPLET_COMMAND_ENTRY_WIDGET_NAME "session_properties_command_entry" +#define CAPPLET_COMMENT_ENTRY_WIDGET_NAME "session_properties_comment_entry" +#define CAPPLET_BROWSE_WIDGET_NAME "session_properties_browse_button" + + +#define GSM_APP_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogPrivate)) + +struct GsmAppDialogPrivate +{ + GtkWidget *name_entry; + GtkWidget *command_entry; + GtkWidget *comment_entry; + GtkWidget *browse_button; + char *name; + char *command; + char *comment; +}; + +static void gsm_app_dialog_class_init (GsmAppDialogClass *klass); +static void gsm_app_dialog_init (GsmAppDialog *app_dialog); +static void gsm_app_dialog_finalize (GObject *object); + +enum { + PROP_0, + PROP_NAME, + PROP_COMMAND, + PROP_COMMENT +}; + +G_DEFINE_TYPE (GsmAppDialog, gsm_app_dialog, GTK_TYPE_DIALOG) + +static char * +make_exec_uri (const char *exec) +{ + GString *str; + const char *c; + + if (exec == NULL) { + return g_strdup (""); + } + + if (strchr (exec, ' ') == NULL) { + return g_strdup (exec); + } + + str = g_string_new_len (NULL, strlen (exec)); + + str = g_string_append_c (str, '"'); + for (c = exec; *c != '\0'; c++) { + /* FIXME: GKeyFile will add an additional backslach so we'll + * end up with toto\\" instead of toto\" + * We could use g_key_file_set_value(), but then we don't + * benefit from the other escaping that glib is doing... + */ + if (*c == '"') { + str = g_string_append (str, "\\\""); + } else { + str = g_string_append_c (str, *c); + } + } + str = g_string_append_c (str, '"'); + + return g_string_free (str, FALSE); +} + +static void +on_browse_button_clicked (GtkWidget *widget, + GsmAppDialog *dialog) +{ + GtkWidget *chooser; + int response; + + chooser = gtk_file_chooser_dialog_new ("", + GTK_WINDOW (dialog), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT, + NULL); + + gtk_window_set_transient_for (GTK_WINDOW (chooser), + GTK_WINDOW (dialog)); + + gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser), TRUE); + + gtk_window_set_title (GTK_WINDOW (chooser), _("Select Command")); + + gtk_widget_show (chooser); + + response = gtk_dialog_run (GTK_DIALOG (chooser)); + + if (response == GTK_RESPONSE_ACCEPT) { + char *text; + char *uri; + + text = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser)); + + uri = make_exec_uri (text); + + g_free (text); + + gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), uri); + + g_free (uri); + } + + gtk_widget_destroy (chooser); +} + +static void +on_entry_activate (GtkEntry *entry, + GsmAppDialog *dialog) +{ + gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); +} + +static void +setup_dialog (GsmAppDialog *dialog) +{ + GtkWidget *content_area; + GtkWidget *widget; + GtkBuilder *xml; + GError *error; + + xml = gtk_builder_new (); + gtk_builder_set_translation_domain (xml, GETTEXT_PACKAGE); + + error = NULL; + if (!gtk_builder_add_from_file (xml, + GTKBUILDER_DIR "/" GTKBUILDER_FILE, + &error)) { + if (error) { + g_warning ("Could not load capplet UI file: %s", + error->message); + g_error_free (error); + } else { + g_warning ("Could not load capplet UI file."); + } + } + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + widget = GTK_WIDGET (gtk_builder_get_object (xml, "main-table")); + gtk_container_add (GTK_CONTAINER (content_area), widget); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties"); + + g_object_set (dialog, + "allow-shrink", FALSE, + "allow-grow", FALSE, + NULL); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + if (dialog->priv->name == NULL + && dialog->priv->command == NULL + && dialog->priv->comment == NULL) { + gtk_window_set_title (GTK_WINDOW (dialog), _("Add Startup Program")); + gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_ADD, GTK_RESPONSE_OK); + } else { + gtk_window_set_title (GTK_WINDOW (dialog), _("Edit Startup Program")); + gtk_dialog_add_button (GTK_DIALOG (dialog), + GTK_STOCK_SAVE, GTK_RESPONSE_OK); + } + + dialog->priv->name_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_NAME_ENTRY_WIDGET_NAME)); + g_signal_connect (dialog->priv->name_entry, + "activate", + G_CALLBACK (on_entry_activate), + dialog); + if (dialog->priv->name != NULL) { + gtk_entry_set_text (GTK_ENTRY (dialog->priv->name_entry), dialog->priv->name); + } + + dialog->priv->browse_button = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_BROWSE_WIDGET_NAME)); + g_signal_connect (dialog->priv->browse_button, + "clicked", + G_CALLBACK (on_browse_button_clicked), + dialog); + + dialog->priv->command_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMAND_ENTRY_WIDGET_NAME)); + g_signal_connect (dialog->priv->command_entry, + "activate", + G_CALLBACK (on_entry_activate), + dialog); + if (dialog->priv->command != NULL) { + gtk_entry_set_text (GTK_ENTRY (dialog->priv->command_entry), dialog->priv->command); + } + + dialog->priv->comment_entry = GTK_WIDGET (gtk_builder_get_object (xml, CAPPLET_COMMENT_ENTRY_WIDGET_NAME)); + g_signal_connect (dialog->priv->comment_entry, + "activate", + G_CALLBACK (on_entry_activate), + dialog); + if (dialog->priv->comment != NULL) { + gtk_entry_set_text (GTK_ENTRY (dialog->priv->comment_entry), dialog->priv->comment); + } + + if (xml != NULL) { + g_object_unref (xml); + } +} + +static GObject * +gsm_app_dialog_constructor (GType type, + guint n_construct_app, + GObjectConstructParam *construct_app) +{ + GsmAppDialog *dialog; + + dialog = GSM_APP_DIALOG (G_OBJECT_CLASS (gsm_app_dialog_parent_class)->constructor (type, + n_construct_app, + construct_app)); + + setup_dialog (dialog); + + gtk_widget_show_all (GTK_WIDGET (dialog)); + + return G_OBJECT (dialog); +} + +static void +gsm_app_dialog_dispose (GObject *object) +{ + GsmAppDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_APP_DIALOG (object)); + + dialog = GSM_APP_DIALOG (object); + + g_free (dialog->priv->name); + dialog->priv->name = NULL; + g_free (dialog->priv->command); + dialog->priv->command = NULL; + g_free (dialog->priv->comment); + dialog->priv->comment = NULL; + + G_OBJECT_CLASS (gsm_app_dialog_parent_class)->dispose (object); +} + +static void +gsm_app_dialog_set_name (GsmAppDialog *dialog, + const char *name) +{ + g_return_if_fail (GSM_IS_APP_DIALOG (dialog)); + + g_free (dialog->priv->name); + + dialog->priv->name = g_strdup (name); + g_object_notify (G_OBJECT (dialog), "name"); +} + +static void +gsm_app_dialog_set_command (GsmAppDialog *dialog, + const char *name) +{ + g_return_if_fail (GSM_IS_APP_DIALOG (dialog)); + + g_free (dialog->priv->command); + + dialog->priv->command = g_strdup (name); + g_object_notify (G_OBJECT (dialog), "command"); +} + +static void +gsm_app_dialog_set_comment (GsmAppDialog *dialog, + const char *name) +{ + g_return_if_fail (GSM_IS_APP_DIALOG (dialog)); + + g_free (dialog->priv->comment); + + dialog->priv->comment = g_strdup (name); + g_object_notify (G_OBJECT (dialog), "comment"); +} + +const char * +gsm_app_dialog_get_name (GsmAppDialog *dialog) +{ + g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL); + return gtk_entry_get_text (GTK_ENTRY (dialog->priv->name_entry)); +} + +const char * +gsm_app_dialog_get_command (GsmAppDialog *dialog) +{ + g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL); + return gtk_entry_get_text (GTK_ENTRY (dialog->priv->command_entry)); +} + +const char * +gsm_app_dialog_get_comment (GsmAppDialog *dialog) +{ + g_return_val_if_fail (GSM_IS_APP_DIALOG (dialog), NULL); + return gtk_entry_get_text (GTK_ENTRY (dialog->priv->comment_entry)); +} + +static void +gsm_app_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GsmAppDialog *dialog = GSM_APP_DIALOG (object); + + switch (prop_id) { + case PROP_NAME: + gsm_app_dialog_set_name (dialog, g_value_get_string (value)); + break; + case PROP_COMMAND: + gsm_app_dialog_set_command (dialog, g_value_get_string (value)); + break; + case PROP_COMMENT: + gsm_app_dialog_set_comment (dialog, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_app_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmAppDialog *dialog = GSM_APP_DIALOG (object); + + switch (prop_id) { + case PROP_NAME: + g_value_set_string (value, dialog->priv->name); + break; + case PROP_COMMAND: + g_value_set_string (value, dialog->priv->command); + break; + case PROP_COMMENT: + g_value_set_string (value, dialog->priv->comment); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gsm_app_dialog_class_init (GsmAppDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gsm_app_dialog_get_property; + object_class->set_property = gsm_app_dialog_set_property; + object_class->constructor = gsm_app_dialog_constructor; + object_class->dispose = gsm_app_dialog_dispose; + object_class->finalize = gsm_app_dialog_finalize; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "name", + "name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_COMMAND, + g_param_spec_string ("command", + "command", + "command", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + g_object_class_install_property (object_class, + PROP_COMMENT, + g_param_spec_string ("comment", + "comment", + "comment", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_type_class_add_private (klass, sizeof (GsmAppDialogPrivate)); +} + +static void +gsm_app_dialog_init (GsmAppDialog *dialog) +{ + + dialog->priv = GSM_APP_DIALOG_GET_PRIVATE (dialog); +} + +static void +gsm_app_dialog_finalize (GObject *object) +{ + GsmAppDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_APP_DIALOG (object)); + + dialog = GSM_APP_DIALOG (object); + + g_return_if_fail (dialog->priv != NULL); + + G_OBJECT_CLASS (gsm_app_dialog_parent_class)->finalize (object); +} + +GtkWidget * +gsm_app_dialog_new (const char *name, + const char *command, + const char *comment) +{ + GObject *object; + + object = g_object_new (GSM_TYPE_APP_DIALOG, + "name", name, + "command", command, + "comment", comment, + NULL); + + return GTK_WIDGET (object); +} + +gboolean +gsm_app_dialog_run (GsmAppDialog *dialog, + char **name_p, + char **command_p, + char **comment_p) +{ + gboolean retval; + + retval = FALSE; + + while (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + const char *name; + const char *exec; + const char *comment; + const char *error_msg; + GError *error; + char **argv; + int argc; + + name = gsm_app_dialog_get_name (GSM_APP_DIALOG (dialog)); + exec = gsm_app_dialog_get_command (GSM_APP_DIALOG (dialog)); + comment = gsm_app_dialog_get_comment (GSM_APP_DIALOG (dialog)); + + error = NULL; + error_msg = NULL; + + if (gsm_util_text_is_blank (exec)) { + error_msg = _("The startup command cannot be empty"); + } else { + if (!g_shell_parse_argv (exec, &argc, &argv, &error)) { + if (error != NULL) { + error_msg = error->message; + } else { + error_msg = _("The startup command is not valid"); + } + } + } + + if (error_msg != NULL) { + GtkWidget *msgbox; + + msgbox = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", error_msg); + + if (error != NULL) { + g_error_free (error); + } + + gtk_dialog_run (GTK_DIALOG (msgbox)); + + gtk_widget_destroy (msgbox); + + continue; + } + + if (gsm_util_text_is_blank (name)) { + name = argv[0]; + } + + if (name_p) { + *name_p = g_strdup (name); + } + + g_strfreev (argv); + + if (command_p) { + *command_p = g_strdup (exec); + } + + if (comment_p) { + *comment_p = g_strdup (comment); + } + + retval = TRUE; + break; + } + + gtk_widget_destroy (GTK_WIDGET (dialog)); + + return retval; +} diff --git a/capplet/gsm-app-dialog.h b/capplet/gsm-app-dialog.h new file mode 100644 index 0000000..ced0628 --- /dev/null +++ b/capplet/gsm-app-dialog.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * + * 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 __GSM_APP_DIALOG_H +#define __GSM_APP_DIALOG_H + +#include +#include + +G_BEGIN_DECLS + +#define GSM_TYPE_APP_DIALOG (gsm_app_dialog_get_type ()) +#define GSM_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_APP_DIALOG, GsmAppDialog)) +#define GSM_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_APP_DIALOG, GsmAppDialogClass)) +#define GSM_IS_APP_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_APP_DIALOG)) +#define GSM_IS_APP_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_APP_DIALOG)) +#define GSM_APP_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_APP_DIALOG, GsmAppDialogClass)) + +typedef struct GsmAppDialogPrivate GsmAppDialogPrivate; + +typedef struct +{ + GtkDialog parent; + GsmAppDialogPrivate *priv; +} GsmAppDialog; + +typedef struct +{ + GtkDialogClass parent_class; +} GsmAppDialogClass; + +GType gsm_app_dialog_get_type (void); + +GtkWidget * gsm_app_dialog_new (const char *name, + const char *command, + const char *comment); + +gboolean gsm_app_dialog_run (GsmAppDialog *dialog, + char **name_p, + char **command_p, + char **comment_p); + +const char * gsm_app_dialog_get_name (GsmAppDialog *dialog); +const char * gsm_app_dialog_get_command (GsmAppDialog *dialog); +const char * gsm_app_dialog_get_comment (GsmAppDialog *dialog); + +G_END_DECLS + +#endif /* __GSM_APP_DIALOG_H */ diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c new file mode 100644 index 0000000..33812b8 --- /dev/null +++ b/capplet/gsm-properties-dialog.c @@ -0,0 +1,774 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2007 Vincent Untz. + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * 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 +#include +#include + +#include "gsm-properties-dialog.h" +#include "gsm-app-dialog.h" +#include "gsm-util.h" +#include "gsp-app.h" +#include "gsp-app-manager.h" + +#define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate)) + +#define GTKBUILDER_FILE "session-properties.ui" + +#define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview" +#define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button" +#define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button" +#define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button" +#define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button" +#define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle" + +#define STARTUP_APP_ICON "system-run" + +#define SPC_SETTINGS_SCHEMA "org.gnome.SessionManager" +#define SPC_SETTINGS_AUTOSAVE_KEY "auto-save-session" + +struct GsmPropertiesDialogPrivate +{ + GtkBuilder *xml; + GtkListStore *list_store; + GtkTreeModel *tree_filter; + + GtkTreeView *treeview; + GtkWidget *add_button; + GtkWidget *delete_button; + GtkWidget *edit_button; + + GSettings *settings; + + GspAppManager *manager; +}; + +enum { + STORE_COL_VISIBLE = 0, + STORE_COL_ENABLED, + STORE_COL_GICON, + STORE_COL_DESCRIPTION, + STORE_COL_APP, + STORE_COL_SEARCH, + NUMBER_OF_COLUMNS +}; + +static void gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass); +static void gsm_properties_dialog_init (GsmPropertiesDialog *properties_dialog); +static void gsm_properties_dialog_finalize (GObject *object); + +G_DEFINE_TYPE (GsmPropertiesDialog, gsm_properties_dialog, GTK_TYPE_DIALOG) + +static gboolean +find_by_app (GtkTreeModel *model, + GtkTreeIter *iter, + GspApp *app) +{ + GspApp *iter_app = NULL; + + if (!gtk_tree_model_get_iter_first (model, iter)) { + return FALSE; + } + + do { + gtk_tree_model_get (model, iter, + STORE_COL_APP, &iter_app, + -1); + + if (iter_app == app) { + g_object_unref (iter_app); + return TRUE; + } + } while (gtk_tree_model_iter_next (model, iter)); + + return FALSE; +} + +static void +_fill_iter_from_app (GtkListStore *list_store, + GtkTreeIter *iter, + GspApp *app) +{ + gboolean hidden; + gboolean display; + gboolean enabled; + gboolean shown; + GIcon *icon; + const char *description; + const char *app_name; + + hidden = gsp_app_get_hidden (app); + display = gsp_app_get_display (app); + enabled = gsp_app_get_enabled (app); + shown = gsp_app_get_shown (app); + icon = gsp_app_get_icon (app); + description = gsp_app_get_description (app); + app_name = gsp_app_get_name (app); + + if (G_IS_THEMED_ICON (icon)) { + GtkIconTheme *theme; + const char * const *icon_names; + + theme = gtk_icon_theme_get_default (); + icon_names = g_themed_icon_get_names (G_THEMED_ICON (icon)); + if (icon_names[0] == NULL || + !gtk_icon_theme_has_icon (theme, icon_names[0])) { + g_object_unref (icon); + icon = NULL; + } + } else if (G_IS_FILE_ICON (icon)) { + GFile *iconfile; + + iconfile = g_file_icon_get_file (G_FILE_ICON (icon)); + if (!g_file_query_exists (iconfile, NULL)) { + g_object_unref (icon); + icon = NULL; + } + } + + if (icon == NULL) { + icon = g_themed_icon_new (STARTUP_APP_ICON); + } + + gtk_list_store_set (list_store, iter, + STORE_COL_VISIBLE, !hidden && shown && display, + STORE_COL_ENABLED, enabled, + STORE_COL_GICON, icon, + STORE_COL_DESCRIPTION, description, + STORE_COL_APP, app, + STORE_COL_SEARCH, app_name, + -1); + g_object_unref (icon); +} + +static void +_app_changed (GsmPropertiesDialog *dialog, + GspApp *app) +{ + GtkTreeIter iter; + + if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store), + &iter, app)) { + return; + } + + _fill_iter_from_app (dialog->priv->list_store, &iter, app); +} + +static void +append_app (GsmPropertiesDialog *dialog, + GspApp *app) +{ + GtkTreeIter iter; + + gtk_list_store_append (dialog->priv->list_store, &iter); + _fill_iter_from_app (dialog->priv->list_store, &iter, app); + + g_signal_connect_swapped (app, "changed", + G_CALLBACK (_app_changed), dialog); +} + +static void +_app_added (GsmPropertiesDialog *dialog, + GspApp *app, + GspAppManager *manager) +{ + append_app (dialog, app); +} + +static void +_app_removed (GsmPropertiesDialog *dialog, + GspApp *app, + GspAppManager *manager) +{ + GtkTreeIter iter; + + if (!find_by_app (GTK_TREE_MODEL (dialog->priv->list_store), + &iter, app)) { + return; + } + + g_signal_handlers_disconnect_by_func (app, + _app_changed, + dialog); + gtk_list_store_remove (dialog->priv->list_store, &iter); +} + +static void +populate_model (GsmPropertiesDialog *dialog) +{ + GSList *apps; + GSList *l; + + apps = gsp_app_manager_get_apps (dialog->priv->manager); + for (l = apps; l != NULL; l = l->next) { + append_app (dialog, GSP_APP (l->data)); + } + g_slist_free (apps); +} + +static void +on_selection_changed (GtkTreeSelection *selection, + GsmPropertiesDialog *dialog) +{ + gboolean sel; + + sel = gtk_tree_selection_get_selected (selection, NULL, NULL); + + gtk_widget_set_sensitive (dialog->priv->edit_button, sel); + gtk_widget_set_sensitive (dialog->priv->delete_button, sel); +} + +static void +on_startup_enabled_toggled (GtkCellRendererToggle *cell_renderer, + char *path, + GsmPropertiesDialog *dialog) +{ + GtkTreeIter iter; + GspApp *app; + gboolean active; + + if (!gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dialog->priv->tree_filter), + &iter, path)) { + return; + } + + app = NULL; + gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), + &iter, + STORE_COL_APP, &app, + -1); + + active = gtk_cell_renderer_toggle_get_active (cell_renderer); + active = !active; + + if (app) { + gsp_app_set_enabled (app, active); + g_object_unref (app); + } +} + +static void +on_drag_data_received (GtkWidget *widget, + GdkDragContext *drag_context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + GsmPropertiesDialog *dialog) +{ + gboolean dnd_success; + + dnd_success = FALSE; + + if (data != NULL) { + char **filenames; + int i; + + filenames = gtk_selection_data_get_uris (data); + + for (i = 0; filenames[i] && filenames[i][0]; i++) { + /* Return success if at least one file succeeded */ + gboolean file_success; + file_success = gsp_app_copy_desktop_file (filenames[i]); + dnd_success = dnd_success || file_success; + } + + g_strfreev (filenames); + } + + gtk_drag_finish (drag_context, dnd_success, FALSE, time); + g_signal_stop_emission_by_name (widget, "drag_data_received"); +} + +static void +on_drag_begin (GtkWidget *widget, + GdkDragContext *context, + GsmPropertiesDialog *dialog) +{ + GtkTreePath *path; + GtkTreeIter iter; + GspApp *app; + + gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &path, NULL); + gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->priv->tree_filter), + &iter, path); + gtk_tree_path_free (path); + + gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), + &iter, + STORE_COL_APP, &app, + -1); + + if (app) { + g_object_set_data_full (G_OBJECT (context), "gsp-app", + g_object_ref (app), g_object_unref); + g_object_unref (app); + } + +} + +static void +on_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + GsmPropertiesDialog *dialog) +{ + GspApp *app; + + app = g_object_get_data (G_OBJECT (context), "gsp-app"); + if (app) { + const char *uris[2]; + char *uri; + + uri = g_filename_to_uri (gsp_app_get_path (app), NULL, NULL); + + uris[0] = uri; + uris[1] = NULL; + gtk_selection_data_set_uris (selection_data, (char **) uris); + + g_free (uri); + } +} + +static void +on_add_app_clicked (GtkWidget *widget, + GsmPropertiesDialog *dialog) +{ + GtkWidget *add_dialog; + char *name; + char *exec; + char *comment; + + add_dialog = gsm_app_dialog_new (NULL, NULL, NULL); + gtk_window_set_transient_for (GTK_WINDOW (add_dialog), + GTK_WINDOW (dialog)); + + if (gsm_app_dialog_run (GSM_APP_DIALOG (add_dialog), + &name, &exec, &comment)) { + gsp_app_create (name, comment, exec); + g_free (name); + g_free (exec); + g_free (comment); + } +} + +static void +on_delete_app_clicked (GtkWidget *widget, + GsmPropertiesDialog *dialog) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + GspApp *app; + + selection = gtk_tree_view_get_selection (dialog->priv->treeview); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { + return; + } + + app = NULL; + gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), + &iter, + STORE_COL_APP, &app, + -1); + + if (app) { + gsp_app_delete (app); + g_object_unref (app); + } +} + +static void +on_edit_app_clicked (GtkWidget *widget, + GsmPropertiesDialog *dialog) +{ + GtkTreeSelection *selection; + GtkTreeIter iter; + GspApp *app; + + selection = gtk_tree_view_get_selection (dialog->priv->treeview); + + if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { + return; + } + + app = NULL; + gtk_tree_model_get (GTK_TREE_MODEL (dialog->priv->tree_filter), + &iter, + STORE_COL_APP, &app, + -1); + + if (app) { + GtkWidget *edit_dialog; + char *name; + char *exec; + char *comment; + + edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app), + gsp_app_get_exec (app), + gsp_app_get_comment (app)); + gtk_window_set_transient_for (GTK_WINDOW (edit_dialog), + GTK_WINDOW (dialog)); + + if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog), + &name, &exec, &comment)) { + gsp_app_update (app, name, comment, exec); + g_free (name); + g_free (exec); + g_free (comment); + } + + g_object_unref (app); + } +} + +static void +on_row_activated (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + GsmPropertiesDialog *dialog) +{ + on_edit_app_clicked (NULL, dialog); +} + +static void +on_save_session_clicked (GtkWidget *widget, + GsmPropertiesDialog *dialog) +{ + g_debug ("Session saving is not implemented yet!"); +} + +static void +setup_dialog (GsmPropertiesDialog *dialog) +{ + GtkTreeView *treeview; + GtkWidget *button; + GtkTreeModel *tree_filter; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSelection *selection; + GtkTargetList *targetlist; + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + + dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, + G_TYPE_ICON, + G_TYPE_STRING, + G_TYPE_OBJECT, + G_TYPE_STRING); + tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store), + NULL); + g_object_unref (dialog->priv->list_store); + dialog->priv->tree_filter = tree_filter; + + gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter), + STORE_COL_VISIBLE); + + treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->priv->xml, + CAPPLET_TREEVIEW_WIDGET_NAME)); + dialog->priv->treeview = treeview; + + gtk_tree_view_set_model (treeview, tree_filter); + g_object_unref (tree_filter); + + gtk_tree_view_set_headers_visible (treeview, FALSE); + g_signal_connect (treeview, + "row-activated", + G_CALLBACK (on_row_activated), + dialog); + + selection = gtk_tree_view_get_selection (treeview); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + g_signal_connect (selection, + "changed", + G_CALLBACK (on_selection_changed), + dialog); + + /* CHECKBOX COLUMN */ + renderer = gtk_cell_renderer_toggle_new (); + column = gtk_tree_view_column_new_with_attributes (_("Enabled"), + renderer, + "active", STORE_COL_ENABLED, + NULL); + gtk_tree_view_append_column (treeview, column); + g_signal_connect (renderer, + "toggled", + G_CALLBACK (on_startup_enabled_toggled), + dialog); + + /* ICON COLUMN */ + renderer = gtk_cell_renderer_pixbuf_new (); + column = gtk_tree_view_column_new_with_attributes (_("Icon"), + renderer, + "gicon", STORE_COL_GICON, + "sensitive", STORE_COL_ENABLED, + NULL); + g_object_set (renderer, + "stock-size", GSM_PROPERTIES_ICON_SIZE, + NULL); + gtk_tree_view_append_column (treeview, column); + + /* NAME COLUMN */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Program"), + renderer, + "markup", STORE_COL_DESCRIPTION, + "sensitive", STORE_COL_ENABLED, + NULL); + g_object_set (renderer, + "ellipsize", PANGO_ELLIPSIZE_END, + NULL); + gtk_tree_view_append_column (treeview, column); + + + gtk_tree_view_column_set_sort_column_id (column, STORE_COL_DESCRIPTION); + gtk_tree_view_set_search_column (treeview, STORE_COL_SEARCH); + gtk_tree_view_set_rules_hint (treeview, TRUE); + + gtk_tree_view_enable_model_drag_source (treeview, + GDK_BUTTON1_MASK|GDK_BUTTON2_MASK, + NULL, 0, + GDK_ACTION_COPY); + gtk_drag_source_add_uri_targets (GTK_WIDGET (treeview)); + + gtk_drag_dest_set (GTK_WIDGET (treeview), + GTK_DEST_DEFAULT_ALL, + NULL, 0, + GDK_ACTION_COPY); + + gtk_drag_dest_add_uri_targets (GTK_WIDGET (treeview)); + /* we don't want to accept drags coming from this widget */ + targetlist = gtk_drag_dest_get_target_list (GTK_WIDGET (treeview)); + if (targetlist != NULL) { + GtkTargetEntry *targets; + gint n_targets; + gint i; + + targets = gtk_target_table_new_from_list (targetlist, &n_targets); + for (i = 0; i < n_targets; i++) + targets[i].flags = GTK_TARGET_OTHER_WIDGET; + + targetlist = gtk_target_list_new (targets, n_targets); + gtk_drag_dest_set_target_list (GTK_WIDGET (treeview), targetlist); + gtk_target_list_unref (targetlist); + + gtk_target_table_free (targets, n_targets); + } + + g_signal_connect (treeview, "drag_begin", + G_CALLBACK (on_drag_begin), + dialog); + g_signal_connect (treeview, "drag_data_get", + G_CALLBACK (on_drag_data_get), + dialog); + g_signal_connect (treeview, "drag_data_received", + G_CALLBACK (on_drag_data_received), + dialog); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->list_store), + STORE_COL_DESCRIPTION, + GTK_SORT_ASCENDING); + + + button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + CAPPLET_ADD_WIDGET_NAME)); + dialog->priv->add_button = button; + g_signal_connect (button, + "clicked", + G_CALLBACK (on_add_app_clicked), + dialog); + + button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + CAPPLET_DELETE_WIDGET_NAME)); + dialog->priv->delete_button = button; + g_signal_connect (button, + "clicked", + G_CALLBACK (on_delete_app_clicked), + dialog); + + button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + CAPPLET_EDIT_WIDGET_NAME)); + dialog->priv->edit_button = button; + g_signal_connect (button, + "clicked", + G_CALLBACK (on_edit_app_clicked), + dialog); + + button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + CAPPLET_REMEMBER_WIDGET_NAME)); + g_settings_bind (dialog->priv->settings, SPC_SETTINGS_AUTOSAVE_KEY, + button, "active", G_SETTINGS_BIND_DEFAULT); + + button = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + CAPPLET_SAVE_WIDGET_NAME)); + g_signal_connect (button, + "clicked", + G_CALLBACK (on_save_session_clicked), + dialog); + + dialog->priv->manager = gsp_app_manager_get (); + gsp_app_manager_fill (dialog->priv->manager); + g_signal_connect_swapped (dialog->priv->manager, "added", + G_CALLBACK (_app_added), dialog); + g_signal_connect_swapped (dialog->priv->manager, "removed", + G_CALLBACK (_app_removed), dialog); + + populate_model (dialog); +} + +static GObject * +gsm_properties_dialog_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + GsmPropertiesDialog *dialog; + + dialog = GSM_PROPERTIES_DIALOG (G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + setup_dialog (dialog); + + gtk_widget_show (GTK_WIDGET (dialog)); + + return G_OBJECT (dialog); +} + +static void +gsm_properties_dialog_dispose (GObject *object) +{ + GsmPropertiesDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object)); + + dialog = GSM_PROPERTIES_DIALOG (object); + + if (dialog->priv->xml != NULL) { + g_object_unref (dialog->priv->xml); + dialog->priv->xml = NULL; + } + + if (dialog->priv->settings != NULL) { + g_object_unref (dialog->priv->settings); + dialog->priv->settings = NULL; + } + + G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->dispose (object); + + /* it's important to do this after chaining to the parent dispose + * method because we want to make sure the treeview has been disposed + * and removed all its references to GspApp objects */ + if (dialog->priv->manager != NULL) { + g_object_unref (dialog->priv->manager); + dialog->priv->manager = NULL; + } +} + +static void +gsm_properties_dialog_class_init (GsmPropertiesDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = gsm_properties_dialog_constructor; + object_class->dispose = gsm_properties_dialog_dispose; + object_class->finalize = gsm_properties_dialog_finalize; + + g_type_class_add_private (klass, sizeof (GsmPropertiesDialogPrivate)); +} + +static void +gsm_properties_dialog_init (GsmPropertiesDialog *dialog) +{ + GtkWidget *content_area; + GtkWidget *widget; + GError *error; + + dialog->priv = GSM_PROPERTIES_DIALOG_GET_PRIVATE (dialog); + + dialog->priv->settings = g_settings_new (SPC_SETTINGS_SCHEMA); + + dialog->priv->xml = gtk_builder_new (); + gtk_builder_set_translation_domain (dialog->priv->xml, GETTEXT_PACKAGE); + + error = NULL; + if (!gtk_builder_add_from_file (dialog->priv->xml, + GTKBUILDER_DIR "/" GTKBUILDER_FILE, + &error)) { + if (error) { + g_warning ("Could not load capplet UI file: %s", + error->message); + g_error_free (error); + } else { + g_warning ("Could not load capplet UI file."); + } + } + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + widget = GTK_WIDGET (gtk_builder_get_object (dialog->priv->xml, + "main-notebook")); + gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0); + + gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 450); + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_box_set_spacing (GTK_BOX (content_area), 2); + gtk_window_set_icon_name (GTK_WINDOW (dialog), "session-properties"); + gtk_window_set_title (GTK_WINDOW (dialog), _("Startup Applications Preferences")); +} + +static void +gsm_properties_dialog_finalize (GObject *object) +{ + GsmPropertiesDialog *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSM_IS_PROPERTIES_DIALOG (object)); + + dialog = GSM_PROPERTIES_DIALOG (object); + + g_return_if_fail (dialog->priv != NULL); + + G_OBJECT_CLASS (gsm_properties_dialog_parent_class)->finalize (object); +} + +GtkWidget * +gsm_properties_dialog_new (void) +{ + GObject *object; + + object = g_object_new (GSM_TYPE_PROPERTIES_DIALOG, + NULL); + + return GTK_WIDGET (object); +} diff --git a/capplet/gsm-properties-dialog.h b/capplet/gsm-properties-dialog.h new file mode 100644 index 0000000..df4915e --- /dev/null +++ b/capplet/gsm-properties-dialog.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2008 William Jon McCann + * + * 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 __GSM_PROPERTIES_DIALOG_H +#define __GSM_PROPERTIES_DIALOG_H + +#include +#include + +G_BEGIN_DECLS + +#define GSM_TYPE_PROPERTIES_DIALOG (gsm_properties_dialog_get_type ()) +#define GSM_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialog)) +#define GSM_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass)) +#define GSM_IS_PROPERTIES_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSM_TYPE_PROPERTIES_DIALOG)) +#define GSM_IS_PROPERTIES_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSM_TYPE_PROPERTIES_DIALOG)) +#define GSM_PROPERTIES_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogClass)) + +typedef struct GsmPropertiesDialogPrivate GsmPropertiesDialogPrivate; + +typedef struct +{ + GtkDialog parent; + GsmPropertiesDialogPrivate *priv; +} GsmPropertiesDialog; + +typedef struct +{ + GtkDialogClass parent_class; +} GsmPropertiesDialogClass; + +GType gsm_properties_dialog_get_type (void); + +GtkWidget * gsm_properties_dialog_new (void); + +#define GSM_PROPERTIES_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR + +G_END_DECLS + +#endif /* __GSM_PROPERTIES_DIALOG_H */ diff --git a/capplet/gsp-app-manager.c b/capplet/gsp-app-manager.c new file mode 100644 index 0000000..bcd6d40 --- /dev/null +++ b/capplet/gsp-app-manager.c @@ -0,0 +1,593 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2007, 2009 Vincent Untz. + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * 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 + +#include "gsm-util.h" +#include "gsp-app.h" + +#include "gsp-app-manager.h" + +static GspAppManager *manager = NULL; + +typedef struct { + char *dir; + int index; + GFileMonitor *monitor; +} GspXdgDir; + +struct _GspAppManagerPrivate { + GSList *apps; + GSList *dirs; +}; + +#define GSP_APP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP_MANAGER, GspAppManagerPrivate)) + + +enum { + ADDED, + REMOVED, + LAST_SIGNAL +}; + +static guint gsp_app_manager_signals[LAST_SIGNAL] = { 0 }; + + +G_DEFINE_TYPE (GspAppManager, gsp_app_manager, G_TYPE_OBJECT) + +static void gsp_app_manager_dispose (GObject *object); +static void gsp_app_manager_finalize (GObject *object); +static void _gsp_app_manager_app_unref (GspApp *app, + GspAppManager *manager); +static void _gsp_app_manager_app_removed (GspAppManager *manager, + GspApp *app); + +static GspXdgDir * +_gsp_xdg_dir_new (const char *dir, + int index) +{ + GspXdgDir *xdgdir; + + xdgdir = g_slice_new (GspXdgDir); + + xdgdir->dir = g_strdup (dir); + xdgdir->index = index; + xdgdir->monitor = NULL; + + return xdgdir; +} + +static void +_gsp_xdg_dir_free (GspXdgDir *xdgdir) +{ + if (xdgdir->dir) { + g_free (xdgdir->dir); + xdgdir->dir = NULL; + } + + if (xdgdir->monitor) { + g_file_monitor_cancel (xdgdir->monitor); + g_object_unref (xdgdir->monitor); + xdgdir->monitor = NULL; + } + + g_slice_free (GspXdgDir, xdgdir); +} + +static void +gsp_app_manager_class_init (GspAppManagerClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + gobject_class->dispose = gsp_app_manager_dispose; + gobject_class->finalize = gsp_app_manager_finalize; + + gsp_app_manager_signals[ADDED] = + g_signal_new ("added", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GspAppManagerClass, + added), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + gsp_app_manager_signals[REMOVED] = + g_signal_new ("removed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GspAppManagerClass, + removed), + NULL, + NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, G_TYPE_OBJECT); + + g_type_class_add_private (class, sizeof (GspAppManagerPrivate)); +} + +static void +gsp_app_manager_init (GspAppManager *manager) +{ + manager->priv = GSP_APP_MANAGER_GET_PRIVATE (manager); + + memset (manager->priv, 0, sizeof (GspAppManagerPrivate)); +} + +static void +gsp_app_manager_dispose (GObject *object) +{ + GspAppManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSP_IS_APP_MANAGER (object)); + + manager = GSP_APP_MANAGER (object); + + /* we unref GspApp objects in dispose since they might need to + * reference us during their dispose/finalize */ + g_slist_foreach (manager->priv->apps, + (GFunc) _gsp_app_manager_app_unref, manager); + g_slist_free (manager->priv->apps); + manager->priv->apps = NULL; + + G_OBJECT_CLASS (gsp_app_manager_parent_class)->dispose (object); +} + +static void +gsp_app_manager_finalize (GObject *object) +{ + GspAppManager *manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSP_IS_APP_MANAGER (object)); + + manager = GSP_APP_MANAGER (object); + + g_slist_foreach (manager->priv->dirs, + (GFunc) _gsp_xdg_dir_free, NULL); + g_slist_free (manager->priv->dirs); + manager->priv->dirs = NULL; + + G_OBJECT_CLASS (gsp_app_manager_parent_class)->finalize (object); + + manager = NULL; +} + +static void +_gsp_app_manager_emit_added (GspAppManager *manager, + GspApp *app) +{ + g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[ADDED], + 0, app); +} + +static void +_gsp_app_manager_emit_removed (GspAppManager *manager, + GspApp *app) +{ + g_signal_emit (G_OBJECT (manager), gsp_app_manager_signals[REMOVED], + 0, app); +} + +/* + * Directories + */ + +static int +gsp_app_manager_get_dir_index (GspAppManager *manager, + const char *dir) +{ + GSList *l; + GspXdgDir *xdgdir; + + g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), -1); + g_return_val_if_fail (dir != NULL, -1); + + for (l = manager->priv->dirs; l != NULL; l = l->next) { + xdgdir = l->data; + if (strcmp (dir, xdgdir->dir) == 0) { + return xdgdir->index; + } + } + + return -1; +} + +const char * +gsp_app_manager_get_dir (GspAppManager *manager, + unsigned int index) +{ + GSList *l; + GspXdgDir *xdgdir; + + g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL); + + for (l = manager->priv->dirs; l != NULL; l = l->next) { + xdgdir = l->data; + if (index == xdgdir->index) { + return xdgdir->dir; + } + } + + return NULL; +} + +static int +_gsp_app_manager_find_dir_with_basename (GspAppManager *manager, + const char *basename, + int minimum_index) +{ + GSList *l; + GspXdgDir *xdgdir; + char *path; + GKeyFile *keyfile; + int result = -1; + + path = NULL; + keyfile = g_key_file_new (); + + for (l = manager->priv->dirs; l != NULL; l = l->next) { + xdgdir = l->data; + + if (xdgdir->index <= minimum_index) { + continue; + } + + g_free (path); + path = g_build_filename (xdgdir->dir, basename, NULL); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + continue; + } + + if (!g_key_file_load_from_file (keyfile, path, + G_KEY_FILE_NONE, NULL)) { + continue; + } + + /* the file exists and is readable */ + if (result == -1) { + result = xdgdir->index; + } else { + result = MIN (result, xdgdir->index); + } + } + + g_key_file_free (keyfile); + g_free (path); + + return result; +} + +static void +_gsp_app_manager_handle_delete (GspAppManager *manager, + GspApp *app, + const char *basename, + int index) +{ + unsigned int position; + unsigned int system_position; + + position = gsp_app_get_xdg_position (app); + system_position = gsp_app_get_xdg_system_position (app); + + if (system_position < index) { + /* it got deleted, but we don't even care about it */ + return; + } + + if (index < position) { + /* it got deleted, but in a position earlier than the current + * one. This happens when the user file was changed and became + * identical to the system file; in this case, the user file is + * simply removed. */ + g_assert (index == 0); + return; + } + + if (position == index && + (system_position == index || system_position == G_MAXUINT)) { + /* the file used by the user was deleted, and there's no other + * file in system directories. So it really got deleted. */ + _gsp_app_manager_app_removed (manager, app); + return; + } + + if (system_position == index) { + /* then we know that position != index; we just hae to tell + * GspApp if there's still a system directory containing this + * basename */ + int new_system; + + new_system = _gsp_app_manager_find_dir_with_basename (manager, + basename, + index); + if (new_system < 0) { + gsp_app_set_xdg_system_position (app, G_MAXUINT); + } else { + gsp_app_set_xdg_system_position (app, new_system); + } + + return; + } + + if (position == index) { + /* then we know that system_position != G_MAXUINT; we need to + * tell GspApp to change position to system_position */ + const char *dir; + + dir = gsp_app_manager_get_dir (manager, system_position); + if (dir) { + char *path; + + path = g_build_filename (dir, basename, NULL); + gsp_app_reload_at (app, path, + (unsigned int) system_position); + g_free (path); + } else { + _gsp_app_manager_app_removed (manager, app); + } + + return; + } + + g_assert_not_reached (); +} + +static gboolean +gsp_app_manager_xdg_dir_monitor (GFileMonitor *monitor, + GFile *child, + GFile *other_file, + GFileMonitorEvent flags, + gpointer data) +{ + GspAppManager *manager; + GspApp *old_app; + GspApp *app; + GFile *parent; + char *basename; + char *dir; + char *path; + int index; + + manager = GSP_APP_MANAGER (data); + + basename = g_file_get_basename (child); + if (!g_str_has_suffix (basename, ".desktop")) { + /* not a desktop file, we can ignore */ + g_free (basename); + return TRUE; + } + old_app = gsp_app_manager_find_app_with_basename (manager, basename); + + parent = g_file_get_parent (child); + dir = g_file_get_path (parent); + g_object_unref (parent); + + index = gsp_app_manager_get_dir_index (manager, dir); + if (index < 0) { + /* not a directory we know; should never happen, though */ + g_free (dir); + return TRUE; + } + + path = g_file_get_path (child); + + switch (flags) { + case G_FILE_MONITOR_EVENT_CHANGED: + case G_FILE_MONITOR_EVENT_CREATED: + /* we just do as if it was a new file: GspApp is clever enough + * to do the right thing */ + app = gsp_app_new (path, (unsigned int) index); + + /* we didn't have this app before, so add it */ + if (old_app == NULL && app != NULL) { + gsp_app_manager_add (manager, app); + g_object_unref (app); + } + /* else: it was just updated, GspApp took care of + * sending the event */ + break; + case G_FILE_MONITOR_EVENT_DELETED: + if (!old_app) { + /* it got deleted, but we don't know about it, so + * nothing to do */ + break; + } + + _gsp_app_manager_handle_delete (manager, old_app, + basename, index); + break; + default: + break; + } + + g_free (path); + g_free (dir); + g_free (basename); + + return TRUE; +} + +/* + * Initialization + */ + +static void +_gsp_app_manager_fill_from_dir (GspAppManager *manager, + GspXdgDir *xdgdir) +{ + GFile *file; + GDir *dir; + const char *name; + + file = g_file_new_for_path (xdgdir->dir); + xdgdir->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, + NULL, NULL); + g_object_unref (file); + + if (xdgdir->monitor) { + g_signal_connect (xdgdir->monitor, "changed", + G_CALLBACK (gsp_app_manager_xdg_dir_monitor), + manager); + } + + dir = g_dir_open (xdgdir->dir, 0, NULL); + if (!dir) { + return; + } + + while ((name = g_dir_read_name (dir))) { + GspApp *app; + char *desktop_file_path; + + if (!g_str_has_suffix (name, ".desktop")) { + continue; + } + + desktop_file_path = g_build_filename (xdgdir->dir, name, NULL); + app = gsp_app_new (desktop_file_path, xdgdir->index); + + if (app != NULL) { + gsp_app_manager_add (manager, app); + g_object_unref (app); + } + + g_free (desktop_file_path); + } + + g_dir_close (dir); +} + +void +gsp_app_manager_fill (GspAppManager *manager) +{ + char **autostart_dirs; + int i; + + if (manager->priv->apps != NULL) + return; + + autostart_dirs = gsm_util_get_autostart_dirs (); + /* we always assume that the first directory is the user one */ + g_assert (g_str_has_prefix (autostart_dirs[0], + g_get_user_config_dir ())); + + for (i = 0; autostart_dirs[i] != NULL; i++) { + GspXdgDir *xdgdir; + + if (gsp_app_manager_get_dir_index (manager, + autostart_dirs[i]) >= 0) { + continue; + } + + xdgdir = _gsp_xdg_dir_new (autostart_dirs[i], i); + manager->priv->dirs = g_slist_prepend (manager->priv->dirs, + xdgdir); + + _gsp_app_manager_fill_from_dir (manager, xdgdir); + } + + g_strfreev (autostart_dirs); +} + +/* + * App handling + */ + +static void +_gsp_app_manager_app_unref (GspApp *app, + GspAppManager *manager) +{ + g_signal_handlers_disconnect_by_func (app, + _gsp_app_manager_app_removed, + manager); + g_object_unref (app); +} + +static void +_gsp_app_manager_app_removed (GspAppManager *manager, + GspApp *app) +{ + _gsp_app_manager_emit_removed (manager, app); + manager->priv->apps = g_slist_remove (manager->priv->apps, app); + _gsp_app_manager_app_unref (app, manager); +} + +void +gsp_app_manager_add (GspAppManager *manager, + GspApp *app) +{ + g_return_if_fail (GSP_IS_APP_MANAGER (manager)); + g_return_if_fail (GSP_IS_APP (app)); + + manager->priv->apps = g_slist_prepend (manager->priv->apps, + g_object_ref (app)); + g_signal_connect_swapped (app, "removed", + G_CALLBACK (_gsp_app_manager_app_removed), + manager); + _gsp_app_manager_emit_added (manager, app); +} + +GspApp * +gsp_app_manager_find_app_with_basename (GspAppManager *manager, + const char *basename) +{ + GSList *l; + GspApp *app; + + g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL); + g_return_val_if_fail (basename != NULL, NULL); + + for (l = manager->priv->apps; l != NULL; l = l->next) { + app = GSP_APP (l->data); + if (strcmp (basename, gsp_app_get_basename (app)) == 0) + return app; + } + + return NULL; +} + +/* + * Singleton + */ + +GspAppManager * +gsp_app_manager_get (void) +{ + if (manager == NULL) { + manager = g_object_new (GSP_TYPE_APP_MANAGER, NULL); + return manager; + } else { + return g_object_ref (manager); + } +} + +GSList * +gsp_app_manager_get_apps (GspAppManager *manager) +{ + g_return_val_if_fail (GSP_IS_APP_MANAGER (manager), NULL); + + return g_slist_copy (manager->priv->apps); +} diff --git a/capplet/gsp-app-manager.h b/capplet/gsp-app-manager.h new file mode 100644 index 0000000..777f8d6 --- /dev/null +++ b/capplet/gsp-app-manager.h @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2007, 2009 Vincent Untz. + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * 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 __GSP_APP_MANAGER_H +#define __GSP_APP_MANAGER_H + +#include + +#include + +G_BEGIN_DECLS + +#define GSP_TYPE_APP_MANAGER (gsp_app_manager_get_type ()) +#define GSP_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP_MANAGER, GspAppManager)) +#define GSP_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP_MANAGER, GspAppManagerClass)) +#define GSP_IS_APP_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP_MANAGER)) +#define GSP_IS_APP_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP_MANAGER)) +#define GSP_APP_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP_MANAGER, GspAppManagerClass)) + +typedef struct _GspAppManager GspAppManager; +typedef struct _GspAppManagerClass GspAppManagerClass; + +typedef struct _GspAppManagerPrivate GspAppManagerPrivate; + +struct _GspAppManagerClass +{ + GObjectClass parent_class; + + void (* added) (GspAppManager *manager, + GspApp *app); + void (* removed) (GspAppManager *manager, + GspApp *app); +}; + +struct _GspAppManager +{ + GObject parent_instance; + + GspAppManagerPrivate *priv; +}; + +GType gsp_app_manager_get_type (void); + +GspAppManager *gsp_app_manager_get (void); + +void gsp_app_manager_fill (GspAppManager *manager); + +GSList *gsp_app_manager_get_apps (GspAppManager *manager); + +GspApp *gsp_app_manager_find_app_with_basename (GspAppManager *manager, + const char *basename); + +const char *gsp_app_manager_get_dir (GspAppManager *manager, + unsigned int index); + +void gsp_app_manager_add (GspAppManager *manager, + GspApp *app); + +G_END_DECLS + +#endif /* __GSP_APP_MANAGER_H */ diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c new file mode 100644 index 0000000..c92b8da --- /dev/null +++ b/capplet/gsp-app.c @@ -0,0 +1,1102 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2007, 2009 Vincent Untz. + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * 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. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include + +#include "gsm-app-dialog.h" +#include "gsm-properties-dialog.h" +#include "gsm-util.h" +#include "gsp-app-manager.h" +#include "gsp-keyfile.h" + +#include "gsp-app.h" + +#define GSP_APP_SAVE_DELAY 2 + +#define GSP_ASP_SAVE_MASK_HIDDEN 0x0001 +#define GSP_ASP_SAVE_MASK_ENABLED 0x0002 +#define GSP_ASP_SAVE_MASK_NAME 0x0004 +#define GSP_ASP_SAVE_MASK_EXEC 0x0008 +#define GSP_ASP_SAVE_MASK_COMMENT 0x0010 +#define GSP_ASP_SAVE_MASK_NO_DISPLAY 0x0020 +#define GSP_ASP_SAVE_MASK_ALL 0xffff + +struct _GspAppPrivate { + char *basename; + char *path; + + gboolean hidden; + gboolean no_display; + gboolean enabled; + gboolean shown; + + char *name; + char *exec; + char *comment; + char *icon; + + GIcon *gicon; + char *description; + + /* position of the directory in the XDG environment variable */ + unsigned int xdg_position; + /* position of the first system directory in the XDG env var containing + * this autostart app too (G_MAXUINT means none) */ + unsigned int xdg_system_position; + + unsigned int save_timeout; + /* mask of what has changed */ + unsigned int save_mask; + /* path that contains the original file that needs to be saved */ + char *old_system_path; + /* after writing to file, we skip the next file monitor event of type + * CHANGED */ + gboolean skip_next_monitor_event; +}; + +#define GSP_APP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSP_TYPE_APP, GspAppPrivate)) + + +enum { + CHANGED, + REMOVED, + LAST_SIGNAL +}; + +static guint gsp_app_signals[LAST_SIGNAL] = { 0 }; + + +G_DEFINE_TYPE (GspApp, gsp_app, G_TYPE_OBJECT) + +static void gsp_app_dispose (GObject *object); +static void gsp_app_finalize (GObject *object); +static gboolean _gsp_app_save (gpointer data); + + +static gboolean +_gsp_str_equal (const char *a, + const char *b) +{ + if (g_strcmp0 (a, b) == 0) { + return TRUE; + } + + if (a && !b && a[0] == '\0') { + return TRUE; + } + + if (b && !a && b[0] == '\0') { + return TRUE; + } + + return FALSE; +} + + +static void +gsp_app_class_init (GspAppClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + gobject_class->dispose = gsp_app_dispose; + gobject_class->finalize = gsp_app_finalize; + + gsp_app_signals[CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GspAppClass, + changed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gsp_app_signals[REMOVED] = + g_signal_new ("removed", + G_TYPE_FROM_CLASS (gobject_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GspAppClass, + removed), + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + g_type_class_add_private (class, sizeof (GspAppPrivate)); +} + +static void +gsp_app_init (GspApp *app) +{ + app->priv = GSP_APP_GET_PRIVATE (app); + + memset (app->priv, 0, sizeof (GspAppPrivate)); + app->priv->xdg_position = G_MAXUINT; + app->priv->xdg_system_position = G_MAXUINT; +} + +static void +_gsp_app_free_reusable_data (GspApp *app) +{ + if (app->priv->path) { + g_free (app->priv->path); + app->priv->path = NULL; + } + + if (app->priv->name) { + g_free (app->priv->name); + app->priv->name = NULL; + } + + if (app->priv->exec) { + g_free (app->priv->exec); + app->priv->exec = NULL; + } + + if (app->priv->comment) { + g_free (app->priv->comment); + app->priv->comment = NULL; + } + + if (app->priv->icon) { + g_free (app->priv->icon); + app->priv->icon = NULL; + } + + if (app->priv->gicon) { + g_object_unref (app->priv->gicon); + app->priv->gicon = NULL; + } + + if (app->priv->description) { + g_free (app->priv->description); + app->priv->description = NULL; + } + + if (app->priv->old_system_path) { + g_free (app->priv->old_system_path); + app->priv->old_system_path = NULL; + } +} + +static void +gsp_app_dispose (GObject *object) +{ + GspApp *app; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSP_IS_APP (object)); + + app = GSP_APP (object); + + /* we save in dispose since we might need to reference GspAppManager */ + if (app->priv->save_timeout) { + g_source_remove (app->priv->save_timeout); + app->priv->save_timeout = 0; + + /* save now */ + _gsp_app_save (app); + } + + G_OBJECT_CLASS (gsp_app_parent_class)->dispose (object); +} + +static void +gsp_app_finalize (GObject *object) +{ + GspApp *app; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSP_IS_APP (object)); + + app = GSP_APP (object); + + if (app->priv->basename) { + g_free (app->priv->basename); + app->priv->basename = NULL; + } + + _gsp_app_free_reusable_data (app); + + G_OBJECT_CLASS (gsp_app_parent_class)->finalize (object); +} + +static void +_gsp_app_emit_changed (GspApp *app) +{ + g_signal_emit (G_OBJECT (app), gsp_app_signals[CHANGED], 0); +} + +static void +_gsp_app_emit_removed (GspApp *app) +{ + g_signal_emit (G_OBJECT (app), gsp_app_signals[REMOVED], 0); +} + +static void +_gsp_app_update_description (GspApp *app) +{ + const char *primary; + const char *secondary; + + if (!gsm_util_text_is_blank (app->priv->name)) { + primary = app->priv->name; + } else if (!gsm_util_text_is_blank (app->priv->exec)) { + primary = app->priv->exec; + } else { + primary = _("No name"); + } + + if (!gsm_util_text_is_blank (app->priv->comment)) { + secondary = app->priv->comment; + } else { + secondary = _("No description"); + } + + g_free (app->priv->description); + app->priv->description = g_markup_printf_escaped ("%s\n%s", + primary, + secondary); +} + +/* + * Saving + */ + +static void +_gsp_ensure_user_autostart_dir (void) +{ + char *dir; + + dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL); + g_mkdir_with_parents (dir, S_IRWXU); + + g_free (dir); +} + +static gboolean +_gsp_app_user_equal_system (GspApp *app, + char **system_path) +{ + GspAppManager *manager; + const char *system_dir; + char *path; + char *str; + GKeyFile *keyfile; + + manager = gsp_app_manager_get (); + system_dir = gsp_app_manager_get_dir (manager, + app->priv->xdg_system_position); + g_object_unref (manager); + if (!system_dir) { + return FALSE; + } + + path = g_build_filename (system_dir, app->priv->basename, NULL); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { + g_free (path); + g_key_file_free (keyfile); + return FALSE; + } + + if (gsp_key_file_get_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_HIDDEN, + FALSE) != app->priv->hidden || + gsp_key_file_get_boolean (keyfile, + GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, + TRUE) != app->priv->enabled || + gsp_key_file_get_shown (keyfile, + gsm_util_get_current_desktop ()) != app->priv->shown) { + g_free (path); + g_key_file_free (keyfile); + return FALSE; + } + + if (gsp_key_file_get_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, + FALSE) != app->priv->no_display) { + g_free (path); + g_key_file_free (keyfile); + return FALSE; + } + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_NAME); + if (!_gsp_str_equal (str, app->priv->name)) { + g_free (str); + g_free (path); + g_key_file_free (keyfile); + return FALSE; + } + g_free (str); + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_COMMENT); + if (!_gsp_str_equal (str, app->priv->comment)) { + g_free (str); + g_free (path); + g_key_file_free (keyfile); + return FALSE; + } + g_free (str); + + str = gsp_key_file_get_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_EXEC); + if (!_gsp_str_equal (str, app->priv->exec)) { + g_free (str); + g_free (path); + g_key_file_free (keyfile); + return FALSE; + } + g_free (str); + + str = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_ICON); + if (!_gsp_str_equal (str, app->priv->icon)) { + g_free (str); + g_free (path); + g_key_file_free (keyfile); + return FALSE; + } + g_free (str); + + g_key_file_free (keyfile); + + *system_path = path; + + return TRUE; +} + +static inline void +_gsp_app_save_done_success (GspApp *app) +{ + app->priv->save_mask = 0; + + if (app->priv->old_system_path) { + g_free (app->priv->old_system_path); + app->priv->old_system_path = NULL; + } +} + +static gboolean +_gsp_app_save (gpointer data) +{ + GspApp *app; + char *use_path; + GKeyFile *keyfile; + GError *error; + + app = GSP_APP (data); + + /* first check if removing the data from the user dir and using the + * data from the system dir is enough -- this helps us keep clean the + * user config dir by removing unneeded files */ + if (_gsp_app_user_equal_system (app, &use_path)) { + if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) { + g_remove (app->priv->path); + } + + g_free (app->priv->path); + app->priv->path = use_path; + + app->priv->xdg_position = app->priv->xdg_system_position; + + _gsp_app_save_done_success (app); + return FALSE; + } + + if (app->priv->old_system_path) + use_path = app->priv->old_system_path; + else + use_path = app->priv->path; + + keyfile = g_key_file_new (); + + error = NULL; + g_key_file_load_from_file (keyfile, use_path, + G_KEY_FILE_KEEP_COMMENTS|G_KEY_FILE_KEEP_TRANSLATIONS, + &error); + + if (error) { + g_error_free (error); + gsp_key_file_populate (keyfile); + } + + if (app->priv->save_mask & GSP_ASP_SAVE_MASK_HIDDEN) { + gsp_key_file_set_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_HIDDEN, + app->priv->hidden); + } + + if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NO_DISPLAY) { + gsp_key_file_set_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, + app->priv->no_display); + } + + if (app->priv->save_mask & GSP_ASP_SAVE_MASK_ENABLED) { + gsp_key_file_set_boolean (keyfile, + GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, + app->priv->enabled); + } + + if (app->priv->save_mask & GSP_ASP_SAVE_MASK_NAME) { + gsp_key_file_set_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_NAME, + app->priv->name); + gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME); + } + + if (app->priv->save_mask & GSP_ASP_SAVE_MASK_COMMENT) { + gsp_key_file_set_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_COMMENT, + app->priv->comment); + gsp_key_file_ensure_C_key (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT); + } + + if (app->priv->save_mask & GSP_ASP_SAVE_MASK_EXEC) { + gsp_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_EXEC, + app->priv->exec); + } + + _gsp_ensure_user_autostart_dir (); + if (gsp_key_file_to_file (keyfile, app->priv->path, NULL)) { + app->priv->skip_next_monitor_event = TRUE; + _gsp_app_save_done_success (app); + } else { + g_warning ("Could not save %s file", app->priv->path); + } + + g_key_file_free (keyfile); + + app->priv->save_timeout = 0; + return FALSE; +} + +static void +_gsp_app_queue_save (GspApp *app) +{ + if (app->priv->save_timeout) { + g_source_remove (app->priv->save_timeout); + app->priv->save_timeout = 0; + } + + /* if the file was not in the user directory, then we'll create a copy + * there */ + if (app->priv->xdg_position != 0) { + app->priv->xdg_position = 0; + + if (app->priv->old_system_path == NULL) { + app->priv->old_system_path = app->priv->path; + /* if old_system_path was not NULL, then it means we + * tried to save and we failed; in that case, we want + * to try again and use the old file as a basis again */ + } + + app->priv->path = g_build_filename (g_get_user_config_dir (), + "autostart", + app->priv->basename, NULL); + } + + app->priv->save_timeout = g_timeout_add_seconds (GSP_APP_SAVE_DELAY, + _gsp_app_save, + app); +} + +/* + * Accessors + */ + +const char * +gsp_app_get_basename (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), NULL); + + return app->priv->basename; +} + +const char * +gsp_app_get_path (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), NULL); + + return app->priv->path; +} + +gboolean +gsp_app_get_hidden (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), FALSE); + + return app->priv->hidden; +} + +gboolean +gsp_app_get_display (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), FALSE); + + return !app->priv->no_display; +} + +gboolean +gsp_app_get_enabled (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), FALSE); + + return app->priv->enabled; +} + +void +gsp_app_set_enabled (GspApp *app, + gboolean enabled) +{ + g_return_if_fail (GSP_IS_APP (app)); + + if (enabled == app->priv->enabled) { + return; + } + + app->priv->enabled = enabled; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED; + + _gsp_app_queue_save (app); + _gsp_app_emit_changed (app); +} + +gboolean +gsp_app_get_shown (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), FALSE); + + return app->priv->shown; +} + +const char * +gsp_app_get_name (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), NULL); + + return app->priv->name; +} + +const char * +gsp_app_get_exec (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), NULL); + + return app->priv->exec; +} + +const char * +gsp_app_get_comment (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), NULL); + + return app->priv->comment; +} + +GIcon * +gsp_app_get_icon (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), NULL); + + if (app->priv->gicon) { + return g_object_ref (app->priv->gicon); + } else { + return NULL; + } +} + +unsigned int +gsp_app_get_xdg_position (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT); + + return app->priv->xdg_position; +} + +unsigned int +gsp_app_get_xdg_system_position (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), G_MAXUINT); + + return app->priv->xdg_system_position; +} + +void +gsp_app_set_xdg_system_position (GspApp *app, + unsigned int position) +{ + g_return_if_fail (GSP_IS_APP (app)); + + app->priv->xdg_system_position = position; +} + +const char * +gsp_app_get_description (GspApp *app) +{ + g_return_val_if_fail (GSP_IS_APP (app), NULL); + + return app->priv->description; +} + +/* + * High-level edition + */ + +void +gsp_app_update (GspApp *app, + const char *name, + const char *comment, + const char *exec) +{ + gboolean changed; + + g_return_if_fail (GSP_IS_APP (app)); + + changed = FALSE; + + if (!_gsp_str_equal (name, app->priv->name)) { + changed = TRUE; + g_free (app->priv->name); + app->priv->name = g_strdup (name); + app->priv->save_mask |= GSP_ASP_SAVE_MASK_NAME; + } + + if (!_gsp_str_equal (comment, app->priv->comment)) { + changed = TRUE; + g_free (app->priv->comment); + app->priv->comment = g_strdup (comment); + app->priv->save_mask |= GSP_ASP_SAVE_MASK_COMMENT; + } + + if (changed) { + _gsp_app_update_description (app); + } + + if (!_gsp_str_equal (exec, app->priv->exec)) { + changed = TRUE; + g_free (app->priv->exec); + app->priv->exec = g_strdup (exec); + app->priv->save_mask |= GSP_ASP_SAVE_MASK_EXEC; + } + + if (changed) { + _gsp_app_queue_save (app); + _gsp_app_emit_changed (app); + } +} + +void +gsp_app_delete (GspApp *app) +{ + g_return_if_fail (GSP_IS_APP (app)); + + if (app->priv->xdg_position == 0 && + app->priv->xdg_system_position == G_MAXUINT) { + /* exists in user directory only */ + if (app->priv->save_timeout) { + g_source_remove (app->priv->save_timeout); + app->priv->save_timeout = 0; + } + + if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) { + g_remove (app->priv->path); + } + + /* for extra safety */ + app->priv->hidden = TRUE; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; + + _gsp_app_emit_removed (app); + } else { + /* also exists in system directory, so we have to keep a file + * in the user directory */ + app->priv->hidden = TRUE; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; + + _gsp_app_queue_save (app); + _gsp_app_emit_changed (app); + } +} + +/* + * New autostart app + */ + +void +gsp_app_reload_at (GspApp *app, + const char *path, + unsigned int xdg_position) +{ + g_return_if_fail (GSP_IS_APP (app)); + + app->priv->xdg_position = G_MAXUINT; + gsp_app_new (path, xdg_position); +} + +GspApp * +gsp_app_new (const char *path, + unsigned int xdg_position) +{ + GspAppManager *manager; + GspApp *app; + GKeyFile *keyfile; + char *basename; + gboolean new; + + basename = g_path_get_basename (path); + + manager = gsp_app_manager_get (); + app = gsp_app_manager_find_app_with_basename (manager, basename); + g_object_unref (manager); + + new = (app == NULL); + + if (!new) { + if (app->priv->xdg_position == xdg_position) { + if (app->priv->skip_next_monitor_event) { + app->priv->skip_next_monitor_event = FALSE; + return NULL; + } + /* else: the file got changed but not by us, we'll + * update our data from disk */ + } + + if (app->priv->xdg_position < xdg_position || + app->priv->save_timeout != 0) { + /* we don't really care about this file, since we + * already have something with a higher priority, or + * we're going to write something in the user config + * anyway. + * Note: xdg_position >= 1 so it's a system dir */ + app->priv->xdg_system_position = MIN (xdg_position, + app->priv->xdg_system_position); + return NULL; + } + } + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { + g_key_file_free (keyfile); + g_free (basename); + return NULL; + } + + if (new) { + app = g_object_new (GSP_TYPE_APP, NULL); + app->priv->basename = basename; + } else { + g_free (basename); + _gsp_app_free_reusable_data (app); + } + + app->priv->path = g_strdup (path); + + app->priv->hidden = gsp_key_file_get_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_HIDDEN, + FALSE); + app->priv->no_display = gsp_key_file_get_boolean (keyfile, + G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, + FALSE); + app->priv->enabled = gsp_key_file_get_boolean (keyfile, + GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, + TRUE); + app->priv->shown = gsp_key_file_get_shown (keyfile, + gsm_util_get_current_desktop ()); + + app->priv->name = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_NAME); + app->priv->exec = gsp_key_file_get_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_EXEC); + app->priv->comment = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_COMMENT); + + if (gsm_util_text_is_blank (app->priv->name)) { + g_free (app->priv->name); + app->priv->name = g_strdup (app->priv->exec); + } + + app->priv->icon = gsp_key_file_get_locale_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_ICON); + + if (app->priv->icon) { + /* look at icon and see if it's a themed icon or not */ + if (g_path_is_absolute (app->priv->icon)) { + GFile *iconfile; + + iconfile = g_file_new_for_path (app->priv->icon); + app->priv->gicon = g_file_icon_new (iconfile); + g_object_unref (iconfile); + } else { + app->priv->gicon = g_themed_icon_new (app->priv->icon); + } + } else { + app->priv->gicon = NULL; + } + + g_key_file_free (keyfile); + + _gsp_app_update_description (app); + + if (xdg_position > 0) { + g_assert (xdg_position <= app->priv->xdg_system_position); + app->priv->xdg_system_position = xdg_position; + } + /* else we keep the old value (which is G_MAXUINT if it wasn't set) */ + app->priv->xdg_position = xdg_position; + + g_assert (!new || app->priv->save_timeout == 0); + app->priv->save_timeout = 0; + app->priv->old_system_path = NULL; + app->priv->skip_next_monitor_event = FALSE; + + if (!new) { + _gsp_app_emit_changed (app); + } + + return app; +} + +static char * +_gsp_find_free_basename (const char *suggested_basename) +{ + GspAppManager *manager; + char *base_path; + char *filename; + char *basename; + int i; + + if (g_str_has_suffix (suggested_basename, ".desktop")) { + char *basename_no_ext; + + basename_no_ext = g_strndup (suggested_basename, + strlen (suggested_basename) - strlen (".desktop")); + base_path = g_build_filename (g_get_user_config_dir (), + "autostart", + basename_no_ext, NULL); + g_free (basename_no_ext); + } else { + base_path = g_build_filename (g_get_user_config_dir (), + "autostart", + suggested_basename, NULL); + } + + filename = g_strdup_printf ("%s.desktop", base_path); + basename = g_path_get_basename (filename); + + manager = gsp_app_manager_get (); + + i = 1; +#define _GSP_FIND_MAX_TRY 10000 + while (gsp_app_manager_find_app_with_basename (manager, + basename) != NULL && + g_file_test (filename, G_FILE_TEST_EXISTS) && + i < _GSP_FIND_MAX_TRY) { + g_free (filename); + g_free (basename); + + filename = g_strdup_printf ("%s-%d.desktop", base_path, i); + basename = g_path_get_basename (filename); + + i++; + } + + g_object_unref (manager); + + g_free (base_path); + g_free (filename); + + if (i == _GSP_FIND_MAX_TRY) { + g_free (basename); + return NULL; + } + + return basename; +} + +void +gsp_app_create (const char *name, + const char *comment, + const char *exec) +{ + GspAppManager *manager; + GspApp *app; + char *basename; + char **argv; + int argc; + + g_return_if_fail (!gsm_util_text_is_blank (exec)); + + if (!g_shell_parse_argv (exec, &argc, &argv, NULL)) { + return; + } + + basename = _gsp_find_free_basename (argv[0]); + g_strfreev (argv); + if (basename == NULL) { + return; + } + + app = g_object_new (GSP_TYPE_APP, NULL); + + app->priv->basename = basename; + app->priv->path = g_build_filename (g_get_user_config_dir (), + "autostart", + app->priv->basename, NULL); + + app->priv->hidden = FALSE; + app->priv->no_display = FALSE; + app->priv->enabled = TRUE; + app->priv->shown = TRUE; + + if (!gsm_util_text_is_blank (name)) { + app->priv->name = g_strdup (name); + } else { + app->priv->name = g_strdup (exec); + } + app->priv->exec = g_strdup (exec); + app->priv->comment = g_strdup (comment); + app->priv->icon = NULL; + + app->priv->gicon = NULL; + _gsp_app_update_description (app); + + /* by definition */ + app->priv->xdg_position = 0; + app->priv->xdg_system_position = G_MAXUINT; + + app->priv->save_timeout = 0; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_ALL; + app->priv->old_system_path = NULL; + app->priv->skip_next_monitor_event = FALSE; + + _gsp_app_queue_save (app); + + manager = gsp_app_manager_get (); + gsp_app_manager_add (manager, app); + g_object_unref (app); + g_object_unref (manager); +} + +gboolean +gsp_app_copy_desktop_file (const char *uri) +{ + GspAppManager *manager; + GspApp *app; + GFile *src_file; + char *src_basename; + char *dst_basename; + char *dst_path; + GFile *dst_file; + gboolean changed; + + g_return_val_if_fail (uri != NULL, FALSE); + + src_file = g_file_new_for_uri (uri); + src_basename = g_file_get_basename (src_file); + + if (src_basename == NULL) { + g_object_unref (src_file); + return FALSE; + } + + dst_basename = _gsp_find_free_basename (src_basename); + g_free (src_basename); + + if (dst_basename == NULL) { + g_object_unref (src_file); + return FALSE; + } + + dst_path = g_build_filename (g_get_user_config_dir (), + "autostart", + dst_basename, NULL); + g_free (dst_basename); + + dst_file = g_file_new_for_path (dst_path); + + _gsp_ensure_user_autostart_dir (); + if (!g_file_copy (src_file, dst_file, G_FILE_COPY_NONE, + NULL, NULL, NULL, NULL)) { + g_object_unref (src_file); + g_object_unref (dst_file); + g_free (dst_path); + return FALSE; + } + + g_object_unref (src_file); + g_object_unref (dst_file); + + app = gsp_app_new (dst_path, 0); + if (!app) { + g_remove (dst_path); + g_free (dst_path); + return FALSE; + } + + g_free (dst_path); + + changed = FALSE; + if (app->priv->hidden) { + changed = TRUE; + app->priv->hidden = FALSE; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; + } + + if (app->priv->no_display) { + changed = TRUE; + app->priv->no_display = FALSE; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_NO_DISPLAY; + } + + if (!app->priv->enabled) { + changed = TRUE; + app->priv->enabled = TRUE; + app->priv->save_mask |= GSP_ASP_SAVE_MASK_ENABLED; + } + + if (changed) { + _gsp_app_queue_save (app); + } + + manager = gsp_app_manager_get (); + gsp_app_manager_add (manager, app); + g_object_unref (app); + g_object_unref (manager); + + return TRUE; +} diff --git a/capplet/gsp-app.h b/capplet/gsp-app.h new file mode 100644 index 0000000..a199795 --- /dev/null +++ b/capplet/gsp-app.h @@ -0,0 +1,108 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2007, 2009 Vincent Untz. + * Copyright (C) 2008 Lucas Rocha. + * Copyright (C) 2008 William Jon McCann + * + * 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 __GSP_APP_H +#define __GSP_APP_H + +#include +#include + +G_BEGIN_DECLS + +#define GSP_TYPE_APP (gsp_app_get_type ()) +#define GSP_APP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSP_TYPE_APP, GspApp)) +#define GSP_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSP_TYPE_APP, GspAppClass)) +#define GSP_IS_APP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSP_TYPE_APP)) +#define GSP_IS_APP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSP_TYPE_APP)) +#define GSP_APP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSP_TYPE_APP, GspAppClass)) + +typedef struct _GspApp GspApp; +typedef struct _GspAppClass GspAppClass; + +typedef struct _GspAppPrivate GspAppPrivate; + +struct _GspAppClass +{ + GObjectClass parent_class; + + void (* changed) (GspApp *app); + void (* removed) (GspApp *app); +}; + +struct _GspApp +{ + GObject parent_instance; + + GspAppPrivate *priv; +}; + +GType gsp_app_get_type (void); + +void gsp_app_create (const char *name, + const char *comment, + const char *exec); +void gsp_app_update (GspApp *app, + const char *name, + const char *comment, + const char *exec); + +gboolean gsp_app_copy_desktop_file (const char *uri); + +void gsp_app_delete (GspApp *app); + +const char *gsp_app_get_basename (GspApp *app); +const char *gsp_app_get_path (GspApp *app); + +gboolean gsp_app_get_hidden (GspApp *app); +gboolean gsp_app_get_display (GspApp *app); + +gboolean gsp_app_get_enabled (GspApp *app); +void gsp_app_set_enabled (GspApp *app, + gboolean enabled); + +gboolean gsp_app_get_shown (GspApp *app); + +const char *gsp_app_get_name (GspApp *app); +const char *gsp_app_get_exec (GspApp *app); +const char *gsp_app_get_comment (GspApp *app); + +const char *gsp_app_get_description (GspApp *app); +GIcon *gsp_app_get_icon (GspApp *app); + +/* private interface for GspAppManager only */ + +GspApp *gsp_app_new (const char *path, + unsigned int xdg_position); + +void gsp_app_reload_at (GspApp *app, + const char *path, + unsigned int xdg_position); + +unsigned int gsp_app_get_xdg_position (GspApp *app); +unsigned int gsp_app_get_xdg_system_position (GspApp *app); +void gsp_app_set_xdg_system_position (GspApp *app, + unsigned int position); + +G_END_DECLS + +#endif /* __GSP_APP_H */ diff --git a/capplet/gsp-keyfile.c b/capplet/gsp-keyfile.c new file mode 100644 index 0000000..de9fac0 --- /dev/null +++ b/capplet/gsp-keyfile.c @@ -0,0 +1,201 @@ +/* + * gsp-keyfile.c: GKeyFile extensions + * + * Copyright (C) 2008, 2009 Novell, Inc. + * + * Based on code from panel-keyfile.c (from gnome-panel) + * + * 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: + * Vincent Untz + */ + +#include + +#include + +#include "gsp-keyfile.h" + +void +gsp_key_file_populate (GKeyFile *keyfile) +{ + gsp_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_TYPE, + "Application"); + + gsp_key_file_set_string (keyfile, + G_KEY_FILE_DESKTOP_KEY_EXEC, + "/bin/false"); +} + +//FIXME: kill this when bug #309224 is fixed +gboolean +gsp_key_file_to_file (GKeyFile *keyfile, + const gchar *path, + GError **error) +{ + GError *write_error; + gchar *data; + gsize length; + gboolean res; + + g_return_val_if_fail (keyfile != NULL, FALSE); + g_return_val_if_fail (path != NULL, FALSE); + + write_error = NULL; + data = g_key_file_to_data (keyfile, &length, &write_error); + + if (write_error) { + g_propagate_error (error, write_error); + return FALSE; + } + + res = g_file_set_contents (path, data, length, &write_error); + g_free (data); + + if (write_error) { + g_propagate_error (error, write_error); + return FALSE; + } + + return res; +} + +gboolean +gsp_key_file_get_boolean (GKeyFile *keyfile, + const gchar *key, + gboolean default_value) +{ + GError *error; + gboolean retval; + + error = NULL; + retval = g_key_file_get_boolean (keyfile, G_KEY_FILE_DESKTOP_GROUP, + key, &error); + if (error != NULL) { + retval = default_value; + g_error_free (error); + } + + return retval; +} + +gboolean +gsp_key_file_get_shown (GKeyFile *keyfile, + const char *current_desktop) +{ + char **only_show_in, **not_show_in; + gboolean found; + int i; + + if (!current_desktop) + return TRUE; + + only_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, + NULL, NULL); + + if (only_show_in) { + found = FALSE; + for (i = 0; only_show_in[i] != NULL; i++) { + if (g_strcmp0 (current_desktop, only_show_in[i]) == 0) { + found = TRUE; + break; + } + } + + g_strfreev (only_show_in); + + if (!found) + return FALSE; + } + + not_show_in = g_key_file_get_string_list (keyfile, G_KEY_FILE_DESKTOP_GROUP, + G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, + NULL, NULL); + + if (not_show_in) { + found = FALSE; + for (i = 0; not_show_in[i] != NULL; i++) { + if (g_strcmp0 (current_desktop, not_show_in[i]) == 0) { + found = TRUE; + break; + } + } + + g_strfreev (not_show_in); + + if (found) + return FALSE; + } + + return TRUE; +} + +void +gsp_key_file_set_locale_string (GKeyFile *keyfile, + const gchar *key, + const gchar *value) +{ + const char *locale; + const char * const *langs_pointer; + int i; + + if (value == NULL) { + value = ""; + } + + locale = NULL; + langs_pointer = g_get_language_names (); + for (i = 0; langs_pointer[i] != NULL; i++) { + /* find first without encoding */ + if (strchr (langs_pointer[i], '.') == NULL) { + locale = langs_pointer[i]; + break; + } + } + + if (locale != NULL) { + g_key_file_set_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, + key, locale, value); + } else { + g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, + key, value); + } +} + +void +gsp_key_file_ensure_C_key (GKeyFile *keyfile, + const char *key) +{ + char *C_value; + char *buffer; + + /* Make sure we set the "C" locale strings to the terms we set here. + * This is so that if the user logs into another locale they get their + * own description there rather then empty. It is not the C locale + * however, but the user created this entry herself so it's OK */ + C_value = gsp_key_file_get_string (keyfile, key); + if (C_value == NULL || C_value [0] == '\0') { + buffer = gsp_key_file_get_locale_string (keyfile, key); + if (buffer) { + gsp_key_file_set_string (keyfile, key, buffer); + g_free (buffer); + } + } + g_free (C_value); +} diff --git a/capplet/gsp-keyfile.h b/capplet/gsp-keyfile.h new file mode 100644 index 0000000..d94f667 --- /dev/null +++ b/capplet/gsp-keyfile.h @@ -0,0 +1,65 @@ +/* + * gsp-keyfile.h: GKeyFile extensions + * + * Copyright (C) 2008, 2009 Novell, Inc. + * + * Based on code from panel-keyfile.h (from gnome-panel) + * + * 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: + * Vincent Untz + */ + +#ifndef GSP_KEYFILE_H +#define GSP_KEYFILE_H + +#include "glib.h" + +G_BEGIN_DECLS + +#define GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED "X-GNOME-Autostart-enabled" + +void gsp_key_file_populate (GKeyFile *keyfile); + +gboolean gsp_key_file_to_file (GKeyFile *keyfile, + const gchar *path, + GError **error); + +gboolean gsp_key_file_get_boolean (GKeyFile *keyfile, + const gchar *key, + gboolean default_value); +gboolean gsp_key_file_get_shown (GKeyFile *keyfile, + const char *current_desktop); +#define gsp_key_file_get_string(key_file, key) \ + g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL) +#define gsp_key_file_get_locale_string(key_file, key) \ + g_key_file_get_locale_string(key_file, G_KEY_FILE_DESKTOP_GROUP, key, NULL, NULL) + +#define gsp_key_file_set_boolean(key_file, key, value) \ + g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value) +#define gsp_key_file_set_string(key_file, key, value) \ + g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP, key, value) +void gsp_key_file_set_locale_string (GKeyFile *keyfile, + const gchar *key, + const gchar *value); + +void gsp_key_file_ensure_C_key (GKeyFile *keyfile, + const char *key); + +G_END_DECLS + +#endif /* GSP_KEYFILE_H */ diff --git a/capplet/main.c b/capplet/main.c new file mode 100644 index 0000000..3c7177b --- /dev/null +++ b/capplet/main.c @@ -0,0 +1,108 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * main.c + * Copyright (C) 1999 Free Software Foundation, Inc. + * Copyright (C) 2008 Lucas Rocha. + * + * 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 + * Lesser 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 + +#include +#include + +#include +#include + +#include "gsm-properties-dialog.h" + +static gboolean show_version = FALSE; + +static GOptionEntry options[] = { + { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, + { NULL, 0, 0, 0, NULL, NULL, NULL } +}; + +static void +dialog_response (GsmPropertiesDialog *dialog, + guint response_id, + gpointer data) +{ + GdkScreen *screen; + GError *error; + + if (response_id == GTK_RESPONSE_HELP) { + screen = gtk_widget_get_screen (GTK_WIDGET (dialog)); + + error = NULL; + gtk_show_uri (screen, "ghelp:user-guide?gosstartsession-2", + gtk_get_current_event_time (), &error); + + if (error != NULL) { + GtkWidget *d; + d = gtk_message_dialog_new (GTK_WINDOW (dialog), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", + _("Could not display help document")); + gtk_message_dialog_format_secondary_text ( + GTK_MESSAGE_DIALOG (d), + "%s", error->message); + g_error_free (error); + + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + } + } else { + gtk_widget_destroy (GTK_WIDGET (dialog)); + gtk_main_quit (); + } +} + +int +main (int argc, char *argv[]) +{ + GError *error; + GtkWidget *dialog; + + bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + error = NULL; + if (! gtk_init_with_args (&argc, &argv, " - GNOME Session Properties", options, GETTEXT_PACKAGE, &error)) { + g_warning ("Unable to start: %s", error->message); + g_error_free (error); + return 1; + } + + if (show_version) { + g_print ("%s %s\n", argv [0], VERSION); + return 0; + } + + dialog = gsm_properties_dialog_new (); + g_signal_connect (dialog, + "response", + G_CALLBACK (dialog_response), + NULL); + gtk_widget_show (dialog); + + gtk_main (); + + return 0; +} diff --git a/configure.ac b/configure.ac index 6485d57..b67c0e6 100644 --- a/configure.ac +++ b/configure.ac @@ -301,65 +301,67 @@ if test $enable_ipv6 = yes; then ]])], [have_ipv6=yes], [have_ipv6=no] ) AC_MSG_RESULT($have_ipv6) dnl ================================================================= dnl Now we would check for specific function like getaddrinfo. dnl ================================================================= have_getaddrinfo=no if test $have_ipv6=yes; then AC_CHECK_FUNC(getaddrinfo, have_getaddrinfo=yes) if test $have_getaddrinfo != yes; then # getaddrinfo is not in the default libraries. See if it's in some other. for lib in bsd socket inet; do AC_CHECK_LIB($lib, getaddrinfo, [LIBS="$LIBS -l$lib";have_getaddrinfo=yes; break]) done fi if test $have_getaddrinfo=yes; then AC_DEFINE(ENABLE_IPV6, 1, [Define if IPV6 is supported]) have_full_ipv6=yes fi fi fi dnl ============================================================================== dnl End of IPv6 checks dnl ============================================================================== AC_CONFIG_FILES([ Makefile +capplet/Makefile doc/Makefile doc/dbus/Makefile doc/dbus/gnome-session.xml doc/man/Makefile data/Makefile +data/gnome-session-properties.desktop.in data/org.gnome.SessionManager.gschema.xml data/icons/Makefile data/icons/16x16/Makefile data/icons/22x22/Makefile data/icons/24x24/Makefile data/icons/32x32/Makefile data/icons/48x48/Makefile data/icons/scalable/Makefile gnome-session/Makefile tools/Makefile po/Makefile.in ]) AC_OUTPUT dnl --------------------------------------------------------------------------- dnl - Show summary dnl --------------------------------------------------------------------------- echo " gnome-session $VERSION `echo gnome-session $VERSION | sed "s/./=/g"` prefix: ${prefix} exec_prefix: ${exec_prefix} libdir: ${libdir} bindir: ${bindir} sbindir: ${sbindir} sysconfdir: ${sysconfdir} localstatedir: ${localstatedir} datadir: ${datadir} diff --git a/data/Makefile.am b/data/Makefile.am index d1d8d25..524cc6e 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,68 +1,74 @@ SUBDIRS = icons uidir = $(pkgdatadir) ui_DATA = \ session-properties.ui if BUILD_SESSION_SELECTOR ui_DATA += session-selector.ui endif hwcompatdir = $(pkgdatadir) hwcompat_DATA = hardware-compatibility xsessiondir = $(datadir)/xsessions xsession_in_files = gnome.desktop.in if BUILD_SESSION_SELECTOR xsession_in_files += gnome-custom-session.desktop.in endif xsession_DATA = $(xsession_in_files:.desktop.in=.desktop) wayland_sessiondir = $(datadir)/wayland-sessions wayland_session_in_files = gnome-wayland.desktop.in wayland_session_DATA = $(wayland_session_in_files:.desktop.in=.desktop) +desktopdir = $(datadir)/applications +desktop_in_files = gnome-session-properties.desktop.in +desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) + sessiondir = $(datadir)/gnome-session/sessions session_in_in_files = gnome.session.desktop.in.in gnome-dummy.session.desktop.in.in gnome-wayland.session.desktop.in.in session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in) session_DATA = $(session_in_files:.session.desktop.in=.session) %.session.desktop.in: %.session.desktop.in.in Makefile $(AM_V_GEN)sed \ -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \ $< > $@ %.session: %.session.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ @INTLTOOL_DESKTOP_RULE@ @INTLTOOL_XML_NOMERGE_RULE@ gsettings_SCHEMAS = org.gnome.SessionManager.gschema.xml @GSETTINGS_RULES@ migrationdir = $(datadir)/GConf/gsettings dist_migration_DATA = gnome-session.convert EXTRA_DIST = \ $(xsession_in_files) \ $(session_in_in_files) \ $(wayland_session_in_files) \ $(gsettings_SCHEMAS:.xml=.xml.in) \ session-selector.ui \ gnome-custom-session.desktop.in \ $(ui_DATA) \ $(hwcompat_DATA) CLEANFILES = \ $(gsettings_SCHEMAS) \ $(xsession_DATA) \ $(wayland_session_DATA) \ + $(desktop_DATA) \ $(session_DATA) DISTCLEANFILES = \ $(gsettings_SCHEMAS) \ + $(desktop_in_files) \ $(wayland_session_in_files) -include $(top_srcdir)/git.mk diff --git a/data/gnome-session-properties.desktop.in.in b/data/gnome-session-properties.desktop.in.in new file mode 100644 index 0000000..3dc7b03 --- /dev/null +++ b/data/gnome-session-properties.desktop.in.in @@ -0,0 +1,15 @@ +[Desktop Entry] +_Name=Startup Applications +_Comment=Choose what applications to start when you log in +Exec=gnome-session-properties +Icon=session-properties +Terminal=false +Type=Application +StartupNotify=true +Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings; +OnlyShowIn=GNOME;Unity; +NoDisplay=true +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-session +X-GNOME-Bugzilla-Component=gnome-session-properties +X-GNOME-Bugzilla-Version=@VERSION@ diff --git a/doc/man/Makefile.am b/doc/man/Makefile.am index 02e5c62..2879423 100644 --- a/doc/man/Makefile.am +++ b/doc/man/Makefile.am @@ -1,29 +1,30 @@ XSLTPROC_FLAGS = \ --nonet \ --stringparam man.output.quietly 1 \ --stringparam funcsynopsis.style ansi \ --stringparam man.th.extra1.suppress 1 \ --stringparam man.authors.section.enabled 0 \ --stringparam man.copyright.section.enabled 0 .xml.1: $(AM_V_GEN) $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $< man_MANS = \ gnome-session.1 \ + gnome-session-properties.1 \ gnome-session-quit.1 \ gnome-session-inhibit.1 if BUILD_SESSION_SELECTOR man_MANS += gnome-session-selector.1 endif EXTRA_DIST = \ gnome-session-inhibit.xml \ gnome-session-selector.xml \ $(man_MANS) CLEANFILES = \ gnome-session-inhibit.1 -include $(top_srcdir)/git.mk diff --git a/doc/man/gnome-session-properties.1 b/doc/man/gnome-session-properties.1 new file mode 100644 index 0000000..c7ef1af --- /dev/null +++ b/doc/man/gnome-session-properties.1 @@ -0,0 +1,24 @@ +.\" +.\" gnome-session-properties manual page. +.\" (C) 2009-2010 Vincent Untz (vuntz@gnome.org) +.\" +.TH GNOME-SESSION-PROPERTIES 1 "GNOME" +.SH NAME +gnome-session-properties \- Configure applications to start on login +.SH SYNOPSIS +.B gnome-session-properties +.SH DESCRIPTION +.PP +The \fIgnome-session-properties\fP program enables the users to +configure what applications should be started on login, in addition to +the default startup applications configured on the system. +.PP +It also proposes an interface to save a snapshot of the currently +running applications so that they can automatically be restored to +their current state on your next GNOME session. +.SH BUGS +If you find bugs in the \fIgnome-session-properties\fP program, please report +these on https://bugzilla.gnome.org. +.SH SEE ALSO +.BR gnome-session(1) +.BR gnome-session-quit(1) diff --git a/po/POTFILES.in b/po/POTFILES.in index 5a887ec..9221412 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,20 +1,25 @@ # List of source files containing translatable strings. # Please keep this file sorted alphabetically. +capplet/gsm-app-dialog.c +capplet/gsm-properties-dialog.c +capplet/gsp-app.c +capplet/main.c data/gnome-custom-session.desktop.in data/gnome.desktop.in data/gnome-dummy.session.desktop.in.in data/gnome.session.desktop.in.in +data/gnome-session-properties.desktop.in.in data/gnome-wayland.desktop.in data/gnome-wayland.session.desktop.in.in [type: gettext/glade]data/session-selector.ui [type: gettext/glade]data/session-properties.ui gnome-session/gsm-fail-whale-dialog.c gnome-session/gsm-manager.c gnome-session/gsm-process-helper.c gnome-session/gsm-util.c gnome-session/gsm-xsmp-client.c gnome-session/gsm-xsmp-server.c gnome-session/main.c tools/gnome-session-inhibit.c tools/gnome-session-selector.c tools/gnome-session-quit.c -- 2.3.7 From 36694037511ac9d61270c7194118cb3fbb438ab7 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 8 May 2015 16:27:15 -0400 Subject: [PATCH 02/17] Revert "Rename the desktop file to gnome-session-properties.desktop" This reverts commit ac9fd0dc97a17674cb082f80df0b1fcc45bc92bf. --- configure.ac | 2 +- data/Makefile.am | 2 +- data/gnome-session-properties.desktop.in.in | 15 --------------- data/session-properties.desktop.in.in | 15 +++++++++++++++ po/POTFILES.in | 1 + po/POTFILES.skip | 3 ++- 6 files changed, 20 insertions(+), 18 deletions(-) delete mode 100644 data/gnome-session-properties.desktop.in.in create mode 100644 data/session-properties.desktop.in.in diff --git a/configure.ac b/configure.ac index b67c0e6..0990e70 100644 --- a/configure.ac +++ b/configure.ac @@ -307,61 +307,61 @@ if test $enable_ipv6 = yes; then dnl ================================================================= dnl Now we would check for specific function like getaddrinfo. dnl ================================================================= have_getaddrinfo=no if test $have_ipv6=yes; then AC_CHECK_FUNC(getaddrinfo, have_getaddrinfo=yes) if test $have_getaddrinfo != yes; then # getaddrinfo is not in the default libraries. See if it's in some other. for lib in bsd socket inet; do AC_CHECK_LIB($lib, getaddrinfo, [LIBS="$LIBS -l$lib";have_getaddrinfo=yes; break]) done fi if test $have_getaddrinfo=yes; then AC_DEFINE(ENABLE_IPV6, 1, [Define if IPV6 is supported]) have_full_ipv6=yes fi fi fi dnl ============================================================================== dnl End of IPv6 checks dnl ============================================================================== AC_CONFIG_FILES([ Makefile capplet/Makefile doc/Makefile doc/dbus/Makefile doc/dbus/gnome-session.xml doc/man/Makefile data/Makefile -data/gnome-session-properties.desktop.in +data/session-properties.desktop.in data/org.gnome.SessionManager.gschema.xml data/icons/Makefile data/icons/16x16/Makefile data/icons/22x22/Makefile data/icons/24x24/Makefile data/icons/32x32/Makefile data/icons/48x48/Makefile data/icons/scalable/Makefile gnome-session/Makefile tools/Makefile po/Makefile.in ]) AC_OUTPUT dnl --------------------------------------------------------------------------- dnl - Show summary dnl --------------------------------------------------------------------------- echo " gnome-session $VERSION `echo gnome-session $VERSION | sed "s/./=/g"` prefix: ${prefix} exec_prefix: ${exec_prefix} libdir: ${libdir} bindir: ${bindir} sbindir: ${sbindir} sysconfdir: ${sysconfdir} localstatedir: ${localstatedir} datadir: ${datadir} diff --git a/data/Makefile.am b/data/Makefile.am index 524cc6e..8834fcb 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,58 +1,58 @@ SUBDIRS = icons uidir = $(pkgdatadir) ui_DATA = \ session-properties.ui if BUILD_SESSION_SELECTOR ui_DATA += session-selector.ui endif hwcompatdir = $(pkgdatadir) hwcompat_DATA = hardware-compatibility xsessiondir = $(datadir)/xsessions xsession_in_files = gnome.desktop.in if BUILD_SESSION_SELECTOR xsession_in_files += gnome-custom-session.desktop.in endif xsession_DATA = $(xsession_in_files:.desktop.in=.desktop) wayland_sessiondir = $(datadir)/wayland-sessions wayland_session_in_files = gnome-wayland.desktop.in wayland_session_DATA = $(wayland_session_in_files:.desktop.in=.desktop) desktopdir = $(datadir)/applications -desktop_in_files = gnome-session-properties.desktop.in +desktop_in_files = session-properties.desktop.in desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) sessiondir = $(datadir)/gnome-session/sessions session_in_in_files = gnome.session.desktop.in.in gnome-dummy.session.desktop.in.in gnome-wayland.session.desktop.in.in session_in_files = $(session_in_in_files:.session.desktop.in.in=.session.desktop.in) session_DATA = $(session_in_files:.session.desktop.in=.session) %.session.desktop.in: %.session.desktop.in.in Makefile $(AM_V_GEN)sed \ -e "s|\@LIBEXECDIR\@|$(libexecdir)|" \ $< > $@ %.session: %.session.desktop.in $(INTLTOOL_MERGE) $(wildcard $(top_srcdir)/po/*.po) ; LC_ALL=C $(INTLTOOL_MERGE) -d -u -c $(top_builddir)/po/.intltool-merge-cache $(top_srcdir)/po $< $@ @INTLTOOL_DESKTOP_RULE@ @INTLTOOL_XML_NOMERGE_RULE@ gsettings_SCHEMAS = org.gnome.SessionManager.gschema.xml @GSETTINGS_RULES@ migrationdir = $(datadir)/GConf/gsettings dist_migration_DATA = gnome-session.convert EXTRA_DIST = \ $(xsession_in_files) \ $(session_in_in_files) \ $(wayland_session_in_files) \ $(gsettings_SCHEMAS:.xml=.xml.in) \ session-selector.ui \ gnome-custom-session.desktop.in \ diff --git a/data/gnome-session-properties.desktop.in.in b/data/gnome-session-properties.desktop.in.in deleted file mode 100644 index 3dc7b03..0000000 --- a/data/gnome-session-properties.desktop.in.in +++ /dev/null @@ -1,15 +0,0 @@ -[Desktop Entry] -_Name=Startup Applications -_Comment=Choose what applications to start when you log in -Exec=gnome-session-properties -Icon=session-properties -Terminal=false -Type=Application -StartupNotify=true -Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings; -OnlyShowIn=GNOME;Unity; -NoDisplay=true -X-GNOME-Bugzilla-Bugzilla=GNOME -X-GNOME-Bugzilla-Product=gnome-session -X-GNOME-Bugzilla-Component=gnome-session-properties -X-GNOME-Bugzilla-Version=@VERSION@ diff --git a/data/session-properties.desktop.in.in b/data/session-properties.desktop.in.in new file mode 100644 index 0000000..3dc7b03 --- /dev/null +++ b/data/session-properties.desktop.in.in @@ -0,0 +1,15 @@ +[Desktop Entry] +_Name=Startup Applications +_Comment=Choose what applications to start when you log in +Exec=gnome-session-properties +Icon=session-properties +Terminal=false +Type=Application +StartupNotify=true +Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings; +OnlyShowIn=GNOME;Unity; +NoDisplay=true +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-session +X-GNOME-Bugzilla-Component=gnome-session-properties +X-GNOME-Bugzilla-Version=@VERSION@ diff --git a/po/POTFILES.in b/po/POTFILES.in index 9221412..b18fcab 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,25 +1,26 @@ # List of source files containing translatable strings. # Please keep this file sorted alphabetically. capplet/gsm-app-dialog.c capplet/gsm-properties-dialog.c capplet/gsp-app.c capplet/main.c data/gnome-custom-session.desktop.in data/gnome.desktop.in data/gnome-dummy.session.desktop.in.in data/gnome.session.desktop.in.in data/gnome-session-properties.desktop.in.in data/gnome-wayland.desktop.in data/gnome-wayland.session.desktop.in.in [type: gettext/glade]data/session-selector.ui +data/session-properties.desktop.in.in [type: gettext/glade]data/session-properties.ui gnome-session/gsm-fail-whale-dialog.c gnome-session/gsm-manager.c gnome-session/gsm-process-helper.c gnome-session/gsm-util.c gnome-session/gsm-xsmp-client.c gnome-session/gsm-xsmp-server.c gnome-session/main.c tools/gnome-session-inhibit.c tools/gnome-session-selector.c tools/gnome-session-quit.c diff --git a/po/POTFILES.skip b/po/POTFILES.skip index c5d4ac9..6f57edc 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -1,9 +1,10 @@ # List of source files containing translatable strings that should not be # translated. # Please keep this file sorted alphabetically. data/gnome-dummy.session.desktop.in data/gnome.session.desktop.in -data/gnome-session-properties.desktop.in +data/session-properties.desktop.in. data/gnome-session.schemas.in data/gnome-wayland.session.desktop.in data/gnome-wm.desktop.in +data/session-properties.desktop.in -- 2.3.7 From 2ed0c1357a3fc729b2f5a86662fb15609c1d81a5 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 13 May 2015 10:56:09 -0400 Subject: [PATCH 03/17] stop using gsm_util_get_current_desktop It no longer exists. --- capplet/gsp-app.c | 111 +++++++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 51 deletions(-) diff --git a/capplet/gsp-app.c b/capplet/gsp-app.c index c92b8da..123ab21 100644 --- a/capplet/gsp-app.c +++ b/capplet/gsp-app.c @@ -3,60 +3,61 @@ * Copyright (C) 1999 Free Software Foundation, Inc. * Copyright (C) 2007, 2009 Vincent Untz. * Copyright (C) 2008 Lucas Rocha. * Copyright (C) 2008 William Jon McCann * * 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. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include +#include #include "gsm-app-dialog.h" #include "gsm-properties-dialog.h" #include "gsm-util.h" #include "gsp-app-manager.h" #include "gsp-keyfile.h" #include "gsp-app.h" #define GSP_APP_SAVE_DELAY 2 #define GSP_ASP_SAVE_MASK_HIDDEN 0x0001 #define GSP_ASP_SAVE_MASK_ENABLED 0x0002 #define GSP_ASP_SAVE_MASK_NAME 0x0004 #define GSP_ASP_SAVE_MASK_EXEC 0x0008 #define GSP_ASP_SAVE_MASK_COMMENT 0x0010 #define GSP_ASP_SAVE_MASK_NO_DISPLAY 0x0020 #define GSP_ASP_SAVE_MASK_ALL 0xffff struct _GspAppPrivate { char *basename; char *path; gboolean hidden; gboolean no_display; gboolean enabled; gboolean shown; char *name; char *exec; @@ -281,148 +282,145 @@ _gsp_app_update_description (GspApp *app) } else { secondary = _("No description"); } g_free (app->priv->description); app->priv->description = g_markup_printf_escaped ("%s\n%s", primary, secondary); } /* * Saving */ static void _gsp_ensure_user_autostart_dir (void) { char *dir; dir = g_build_filename (g_get_user_config_dir (), "autostart", NULL); g_mkdir_with_parents (dir, S_IRWXU); g_free (dir); } static gboolean _gsp_app_user_equal_system (GspApp *app, char **system_path) { GspAppManager *manager; - const char *system_dir; - char *path; - char *str; - GKeyFile *keyfile; + gboolean return_value = FALSE; + const char *system_dir = NULL; + char *path = NULL; + char *str = NULL; + GKeyFile *keyfile = NULL; + GDesktopAppInfo *app_info = NULL; manager = gsp_app_manager_get (); system_dir = gsp_app_manager_get_dir (manager, app->priv->xdg_system_position); g_object_unref (manager); if (!system_dir) { - return FALSE; + goto out; } path = g_build_filename (system_dir, app->priv->basename, NULL); keyfile = g_key_file_new (); if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { - g_free (path); - g_key_file_free (keyfile); - return FALSE; + goto out; } - if (gsp_key_file_get_boolean (keyfile, - G_KEY_FILE_DESKTOP_KEY_HIDDEN, - FALSE) != app->priv->hidden || - gsp_key_file_get_boolean (keyfile, - GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, - TRUE) != app->priv->enabled || - gsp_key_file_get_shown (keyfile, - gsm_util_get_current_desktop ()) != app->priv->shown) { - g_free (path); - g_key_file_free (keyfile); - return FALSE; + app_info = g_desktop_app_info_new_from_keyfile (keyfile); + + if (!app_info) { + goto out; } - if (gsp_key_file_get_boolean (keyfile, - G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, - FALSE) != app->priv->no_display) { - g_free (path); - g_key_file_free (keyfile); - return FALSE; + if (g_desktop_app_info_get_is_hidden (app_info)) { + goto out; + } + + if (g_desktop_app_info_has_key (app_info, + GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED) && + !g_desktop_app_info_get_boolean (app_info, + GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED)) { + goto out; + } + + if (!g_desktop_app_info_get_show_in (app_info, NULL)) { + goto out; + } + + if (g_desktop_app_info_get_nodisplay (app_info)) { + goto out; } str = gsp_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME); if (!_gsp_str_equal (str, app->priv->name)) { - g_free (str); - g_free (path); - g_key_file_free (keyfile); - return FALSE; + goto out; } - g_free (str); + g_clear_pointer (&str, g_free); str = gsp_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT); if (!_gsp_str_equal (str, app->priv->comment)) { - g_free (str); - g_free (path); - g_key_file_free (keyfile); - return FALSE; + goto out; } - g_free (str); + g_clear_pointer (&str, g_free); str = gsp_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_KEY_EXEC); if (!_gsp_str_equal (str, app->priv->exec)) { - g_free (str); - g_free (path); - g_key_file_free (keyfile); - return FALSE; + goto out; } - g_free (str); + g_clear_pointer (&str, g_free); str = gsp_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_KEY_ICON); if (!_gsp_str_equal (str, app->priv->icon)) { - g_free (str); - g_free (path); - g_key_file_free (keyfile); - return FALSE; + goto out; } - g_free (str); - - g_key_file_free (keyfile); + g_clear_pointer (&str, g_free); *system_path = path; - - return TRUE; + path = NULL; + return_value = TRUE; +out: + g_clear_pointer (&path, g_free); + g_clear_pointer (&str, g_free); + g_clear_object (&app_info); + g_clear_pointer (&keyfile, g_key_file_free); + + return return_value; } static inline void _gsp_app_save_done_success (GspApp *app) { app->priv->save_mask = 0; if (app->priv->old_system_path) { g_free (app->priv->old_system_path); app->priv->old_system_path = NULL; } } static gboolean _gsp_app_save (gpointer data) { GspApp *app; char *use_path; GKeyFile *keyfile; GError *error; app = GSP_APP (data); /* first check if removing the data from the user dir and using the * data from the system dir is enough -- this helps us keep clean the * user config dir by removing unneeded files */ if (_gsp_app_user_equal_system (app, &use_path)) { if (g_file_test (app->priv->path, G_FILE_TEST_EXISTS)) { g_remove (app->priv->path); } @@ -748,153 +746,164 @@ gsp_app_delete (GspApp *app) app->priv->hidden = TRUE; app->priv->save_mask |= GSP_ASP_SAVE_MASK_HIDDEN; _gsp_app_queue_save (app); _gsp_app_emit_changed (app); } } /* * New autostart app */ void gsp_app_reload_at (GspApp *app, const char *path, unsigned int xdg_position) { g_return_if_fail (GSP_IS_APP (app)); app->priv->xdg_position = G_MAXUINT; gsp_app_new (path, xdg_position); } GspApp * gsp_app_new (const char *path, unsigned int xdg_position) { GspAppManager *manager; GspApp *app; GKeyFile *keyfile; + GDesktopAppInfo *app_info; char *basename; gboolean new; basename = g_path_get_basename (path); manager = gsp_app_manager_get (); app = gsp_app_manager_find_app_with_basename (manager, basename); g_object_unref (manager); new = (app == NULL); if (!new) { if (app->priv->xdg_position == xdg_position) { if (app->priv->skip_next_monitor_event) { app->priv->skip_next_monitor_event = FALSE; return NULL; } /* else: the file got changed but not by us, we'll * update our data from disk */ } if (app->priv->xdg_position < xdg_position || app->priv->save_timeout != 0) { /* we don't really care about this file, since we * already have something with a higher priority, or * we're going to write something in the user config * anyway. * Note: xdg_position >= 1 so it's a system dir */ app->priv->xdg_system_position = MIN (xdg_position, app->priv->xdg_system_position); return NULL; } } keyfile = g_key_file_new (); if (!g_key_file_load_from_file (keyfile, path, G_KEY_FILE_NONE, NULL)) { g_key_file_free (keyfile); g_free (basename); return NULL; } + app_info = g_desktop_app_info_new_from_keyfile (keyfile); + + if (!app_info) { + g_object_unref (app_info); + g_key_file_free (keyfile); + g_free (basename); + return NULL; + } + if (new) { app = g_object_new (GSP_TYPE_APP, NULL); app->priv->basename = basename; } else { g_free (basename); _gsp_app_free_reusable_data (app); } + app->priv->path = g_strdup (path); app->priv->hidden = gsp_key_file_get_boolean (keyfile, G_KEY_FILE_DESKTOP_KEY_HIDDEN, FALSE); app->priv->no_display = gsp_key_file_get_boolean (keyfile, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, FALSE); app->priv->enabled = gsp_key_file_get_boolean (keyfile, GSP_KEY_FILE_DESKTOP_KEY_AUTOSTART_ENABLED, TRUE); - app->priv->shown = gsp_key_file_get_shown (keyfile, - gsm_util_get_current_desktop ()); + app->priv->shown = g_desktop_app_info_get_show_in (app_info, NULL); app->priv->name = gsp_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_KEY_NAME); app->priv->exec = gsp_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_KEY_EXEC); app->priv->comment = gsp_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_KEY_COMMENT); if (gsm_util_text_is_blank (app->priv->name)) { g_free (app->priv->name); app->priv->name = g_strdup (app->priv->exec); } app->priv->icon = gsp_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_KEY_ICON); if (app->priv->icon) { /* look at icon and see if it's a themed icon or not */ if (g_path_is_absolute (app->priv->icon)) { GFile *iconfile; iconfile = g_file_new_for_path (app->priv->icon); app->priv->gicon = g_file_icon_new (iconfile); g_object_unref (iconfile); } else { app->priv->gicon = g_themed_icon_new (app->priv->icon); } } else { app->priv->gicon = NULL; } + g_object_unref (app_info); g_key_file_free (keyfile); _gsp_app_update_description (app); if (xdg_position > 0) { g_assert (xdg_position <= app->priv->xdg_system_position); app->priv->xdg_system_position = xdg_position; } /* else we keep the old value (which is G_MAXUINT if it wasn't set) */ app->priv->xdg_position = xdg_position; g_assert (!new || app->priv->save_timeout == 0); app->priv->save_timeout = 0; app->priv->old_system_path = NULL; app->priv->skip_next_monitor_event = FALSE; if (!new) { _gsp_app_emit_changed (app); } return app; } static char * _gsp_find_free_basename (const char *suggested_basename) { GspAppManager *manager; char *base_path; char *filename; char *basename; -- 2.3.7 From 6b24a33c89913eec63a1547d5e372833e5c25a30 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 10:48:03 -0500 Subject: [PATCH 04/17] session-properties: show it in the menus again It provides functionality for adjusting the session which customers rely on, so make it show up in the menus again. --- data/session-properties.desktop.in.in | 1 - 1 file changed, 1 deletion(-) diff --git a/data/session-properties.desktop.in.in b/data/session-properties.desktop.in.in index 3dc7b03..1a506d9 100644 --- a/data/session-properties.desktop.in.in +++ b/data/session-properties.desktop.in.in @@ -1,15 +1,14 @@ [Desktop Entry] _Name=Startup Applications _Comment=Choose what applications to start when you log in Exec=gnome-session-properties Icon=session-properties Terminal=false Type=Application StartupNotify=true Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings; OnlyShowIn=GNOME;Unity; -NoDisplay=true X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=gnome-session X-GNOME-Bugzilla-Component=gnome-session-properties X-GNOME-Bugzilla-Version=@VERSION@ -- 2.3.7 From 9fe478de117315c714d63a73b6c143b50a57912e Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 15:07:35 -0500 Subject: [PATCH 05/17] session-properties: get out of Other Put it in the menus next to Settings and Software --- data/session-properties.desktop.in.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/session-properties.desktop.in.in b/data/session-properties.desktop.in.in index 1a506d9..e615d5c 100644 --- a/data/session-properties.desktop.in.in +++ b/data/session-properties.desktop.in.in @@ -1,14 +1,14 @@ [Desktop Entry] _Name=Startup Applications _Comment=Choose what applications to start when you log in Exec=gnome-session-properties Icon=session-properties Terminal=false Type=Application StartupNotify=true -Categories=GTK;GNOME;Settings;X-GNOME-PersonalSettings; +Categories=GNOME;GTK;System; OnlyShowIn=GNOME;Unity; X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=gnome-session X-GNOME-Bugzilla-Component=gnome-session-properties X-GNOME-Bugzilla-Version=@VERSION@ -- 2.3.7 From 0e2261200550ebe0a0e455b550b227d12c47ac50 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 10:53:33 -0500 Subject: [PATCH 06/17] session-properties: refresh from recent glade The ui file is rather old. This commit just opens it up in a recent glade and resaves it, so we have a fresh starting point to make changes. --- data/session-properties.ui | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/data/session-properties.ui b/data/session-properties.ui index 1f0cb9a..47a30f7 100644 --- a/data/session-properties.ui +++ b/data/session-properties.ui @@ -1,323 +1,348 @@ - + - - + True True 6 True + False 12 - vertical 3 True + False 0 3 3 Additional startup _programs: True session_properties_treeview False + True 0 True + False 6 True True never - automatic etched-in 210 True True + + + + True + True 0 True + False 6 start gtk-add True True True True False False 0 gtk-remove True False True True True False False 1 gtk-edit True False True True True False False 2 False False 1 + True + True 1 True + False Startup Programs False True + False 12 - vertical 6 _Automatically remember running applications when logging out True True False True + 0.5 True False False 0 True + False True True True + False 4 True + False gtk-save False False 0 True + False _Remember Currently Running Applications True + True + True 1 False False 0 False False 1 1 True + False Options 1 False True + False 6 3 2 12 6 True + False 12 True True - + ā— + True + True 0 Browseā€¦ True True True False False 1 1 2 1 2 GTK_FILL True True - + ā— 1 2 2 3 GTK_FILL True True - + ā— 1 2 GTK_FILL True + False 0 Comm_ent: True label2 2 3 GTK_FILL GTK_FILL True + False 0 Co_mmand: True session_properties_command_entry 1 2 GTK_FILL GTK_FILL True + False 0 _Name: True session_properties_name_entry GTK_FILL GTK_FILL -- 2.3.7 From 70e758b03d9b4a018c196cf58d4147709ae1eb5e Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 16:14:16 -0500 Subject: [PATCH 07/17] manager: Don't clear saved session if autosaving is disabled Now that we support on-demand saving again, we need to make sure we don't wipe that away at log out. --- gnome-session/gsm-manager.c | 1 - 1 file changed, 1 deletion(-) diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c index 596db92..c32bbe4 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -1952,61 +1952,60 @@ on_xsmp_client_register_confirmed (GsmXSMPClient *client, app = find_app_for_startup_id (manager, id); if (app != NULL) { gsm_app_registered (app); } } static gboolean auto_save_is_enabled (GsmManager *manager) { return g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE_ONE_SHOT) || g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE); } static void maybe_save_session (GsmManager *manager) { GError *error; if (gsm_system_is_login_session (manager->priv->system)) return; /* We only allow session saving when session is running or when * logging out */ if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { return; } if (!auto_save_is_enabled (manager)) { - gsm_session_save_clear (); return; } error = NULL; gsm_session_save (manager->priv->clients, &error); if (error) { g_warning ("Error saving session: %s", error->message); g_error_free (error); } } static void _handle_client_end_session_response (GsmManager *manager, GsmClient *client, gboolean is_ok, gboolean do_last, gboolean cancel, const char *reason) { /* just ignore if received outside of shutdown */ if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) { return; } g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); if (cancel) { cancel_end_session (manager); return; -- 2.3.7 From 4d41334693ccf1617f242326d1c5a9e367a54169 Mon Sep 17 00:00:00 2001 From: Josselin Mouette Date: Mon, 21 Jun 2010 15:22:23 -0400 Subject: [PATCH 08/17] Add "Remember Currently Running Applications" button This adds back session saving that's not at logout. --- capplet/gsm-properties-dialog.c | 63 +++++++++++- configure.ac | 1 + data/session-properties.ui | 12 +++ gnome-session/gsm-client.c | 10 ++ gnome-session/gsm-client.h | 6 ++ gnome-session/gsm-dbus-client.c | 14 +++ gnome-session/gsm-manager.c | 150 ++++++++++++++++++++++++++++- gnome-session/gsm-manager.h | 3 + gnome-session/gsm-xsmp-client.c | 37 +++++++ gnome-session/gsm-xsmp-client.h | 3 +- gnome-session/org.gnome.SessionManager.xml | 8 ++ 11 files changed, 303 insertions(+), 4 deletions(-) diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c index 33812b8..d2be778 100644 --- a/capplet/gsm-properties-dialog.c +++ b/capplet/gsm-properties-dialog.c @@ -5,70 +5,77 @@ * Copyright (C) 2008 Lucas Rocha. * Copyright (C) 2008 William Jon McCann * * 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 #include #include #include "gsm-properties-dialog.h" #include "gsm-app-dialog.h" #include "gsm-util.h" #include "gsp-app.h" #include "gsp-app-manager.h" +#include +#include + +#define GSM_SERVICE_DBUS "org.gnome.SessionManager" +#define GSM_PATH_DBUS "/org/gnome/SessionManager" +#define GSM_INTERFACE_DBUS "org.gnome.SessionManager" #define GSM_PROPERTIES_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_PROPERTIES_DIALOG, GsmPropertiesDialogPrivate)) #define GTKBUILDER_FILE "session-properties.ui" #define CAPPLET_TREEVIEW_WIDGET_NAME "session_properties_treeview" #define CAPPLET_ADD_WIDGET_NAME "session_properties_add_button" #define CAPPLET_DELETE_WIDGET_NAME "session_properties_delete_button" #define CAPPLET_EDIT_WIDGET_NAME "session_properties_edit_button" #define CAPPLET_SAVE_WIDGET_NAME "session_properties_save_button" +#define CAPPLET_SESSION_SAVED_WIDGET_NAME "session_properties_session_saved_label" #define CAPPLET_REMEMBER_WIDGET_NAME "session_properties_remember_toggle" #define STARTUP_APP_ICON "system-run" #define SPC_SETTINGS_SCHEMA "org.gnome.SessionManager" #define SPC_SETTINGS_AUTOSAVE_KEY "auto-save-session" struct GsmPropertiesDialogPrivate { GtkBuilder *xml; GtkListStore *list_store; GtkTreeModel *tree_filter; GtkTreeView *treeview; GtkWidget *add_button; GtkWidget *delete_button; GtkWidget *edit_button; GSettings *settings; GspAppManager *manager; }; enum { STORE_COL_VISIBLE = 0, STORE_COL_ENABLED, STORE_COL_GICON, STORE_COL_DESCRIPTION, STORE_COL_APP, STORE_COL_SEARCH, @@ -431,64 +438,118 @@ on_edit_app_clicked (GtkWidget *widget, char *comment; edit_dialog = gsm_app_dialog_new (gsp_app_get_name (app), gsp_app_get_exec (app), gsp_app_get_comment (app)); gtk_window_set_transient_for (GTK_WINDOW (edit_dialog), GTK_WINDOW (dialog)); if (gsm_app_dialog_run (GSM_APP_DIALOG (edit_dialog), &name, &exec, &comment)) { gsp_app_update (app, name, comment, exec); g_free (name); g_free (exec); g_free (comment); } g_object_unref (app); } } static void on_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, GsmPropertiesDialog *dialog) { on_edit_app_clicked (NULL, dialog); } static void +session_saved_message (GsmPropertiesDialog *dialog, + const char *msg, + gboolean is_error) +{ + GtkLabel *label; + gchar *markup; + label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME)); + if (is_error) + markup = g_markup_printf_escaped ("%s", msg); + else + markup = g_markup_escape_text (msg, -1); + gtk_label_set_markup (label, markup); + g_free (markup); +} + +static void +session_saved_cb (DBusGProxy *proxy, + DBusGProxyCall *call_id, + void *user_data) +{ + gboolean res; + GsmPropertiesDialog *dialog = user_data; + + res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID); + if (res) + session_saved_message (dialog, _("Your session has been saved."), FALSE); + else + session_saved_message (dialog, _("Failed to save session"), TRUE); + + g_object_unref (proxy); +} + +static void on_save_session_clicked (GtkWidget *widget, GsmPropertiesDialog *dialog) { - g_debug ("Session saving is not implemented yet!"); + DBusGConnection *conn; + DBusGProxy *proxy; + DBusGProxyCall *call; + + conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (conn == NULL) { + session_saved_message (dialog, _("Could not connect to the session bus"), TRUE); + return; + } + + proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); + if (proxy == NULL) { + session_saved_message (dialog, _("Could not connect to the session manager"), TRUE); + return; + } + + call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID); + if (call == NULL) { + session_saved_message (dialog, _("Failed to save session"), TRUE); + g_object_unref (proxy); + return; + } } static void setup_dialog (GsmPropertiesDialog *dialog) { GtkTreeView *treeview; GtkWidget *button; GtkTreeModel *tree_filter; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeSelection *selection; GtkTargetList *targetlist; gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_ICON, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_STRING); tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store), NULL); g_object_unref (dialog->priv->list_store); dialog->priv->tree_filter = tree_filter; gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter), diff --git a/configure.ac b/configure.ac index 0990e70..505292c 100644 --- a/configure.ac +++ b/configure.ac @@ -52,60 +52,61 @@ if test "$enable_session_selector" = yes; then fi dnl ==================================================================== dnl Dependency Checks dnl ==================================================================== dnl Standard vertical stacks PKG_CHECK_MODULES(GIO, gio-2.0) PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= $GLIB_REQUIRED) PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK3_REQUIRED) PKG_CHECK_MODULES(GNOME_SESSION, glib-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED dbus-glib-1 >= $DBUS_GLIB_REQUIRED json-glib-1.0 >= $JSON_GLIB_REQUIRED gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED ) dnl We can only support old upower dnl https://bugzilla.gnome.org/show_bug.cgi?id=710383 PKG_CHECK_MODULES(UPOWER, upower-glib < 0.99.0, have_old_upower=yes, have_old_upower=no) AS_IF([test x$have_old_upower = xyes], [ AC_DEFINE([HAVE_OLD_UPOWER], [1], [Define if we have an older upower]) ]) AM_CONDITIONAL(HAVE_OLD_UPOWER, test x$have_old_upower = xyes) PKG_CHECK_MODULES(SESSION_PROPERTIES, glib-2.0 >= $GLIB_REQUIRED gtk+-3.0 >= $GTK3_REQUIRED + dbus-glib-1 >= $DBUS_GLIB_REQUIRED ) PKG_CHECK_MODULES(X11, x11) PKG_CHECK_MODULES(SM, sm) PKG_CHECK_MODULES(ICE, ice) PKG_CHECK_MODULES(XEXT, xext xau) PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= $DBUS_GLIB_REQUIRED) PKG_CHECK_MODULES(EGG_SMCLIENT, gtk+-3.0) PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0) dnl ==================================================================== dnl Check for gconf dnl ==================================================================== AC_ARG_ENABLE([gconf], AS_HELP_STRING([--enable-gconf], [Support gconf-based autostart]), [enable_gconf=$enableval], [enable_gconf=auto]) PKG_CHECK_MODULES(GCONF, gconf-2.0, [have_gconf=yes], [have_gconf=no]) if test x$enable_gconf = xauto ; then enable_gconf=$have_gconf elif test x$enable_gconf = xyes -a x$have_gconf = xno ; then AC_MSG_ERROR([GConf support explicitly required, but gconf not found]) fi if test x$enable_gconf = xyes ; then diff --git a/data/session-properties.ui b/data/session-properties.ui index 47a30f7..b43759f 100644 --- a/data/session-properties.ui +++ b/data/session-properties.ui @@ -133,108 +133,120 @@ True False 12 6 _Automatically remember running applications when logging out True True False True 0.5 True False False 0 True False + True True True True False 4 True False gtk-save False False 0 True False _Remember Currently Running Applications True True True 1 False False 0 False False 1 + + + True + True + + + False + False + 2 + + 1 True False Options 1 False True False 6 3 2 12 6 True False 12 diff --git a/gnome-session/gsm-client.c b/gnome-session/gsm-client.c index 543d7ef..23f0f56 100644 --- a/gnome-session/gsm-client.c +++ b/gnome-session/gsm-client.c @@ -493,49 +493,59 @@ gsm_client_query_end_session (GsmClient *client, g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); return GSM_CLIENT_GET_CLASS (client)->impl_query_end_session (client, flags, error); } gboolean gsm_client_end_session (GsmClient *client, GsmClientEndSessionFlag flags, GError **error) { g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); return GSM_CLIENT_GET_CLASS (client)->impl_end_session (client, flags, error); } gboolean gsm_client_stop (GsmClient *client, GError **error) { g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); return GSM_CLIENT_GET_CLASS (client)->impl_stop (client, error); } void gsm_client_disconnected (GsmClient *client) { g_signal_emit (client, signals[DISCONNECTED], 0); } +gboolean +gsm_client_request_save (GsmClient *client, + guint flags, + GError **error) +{ + g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); + + return GSM_CLIENT_GET_CLASS (client)->impl_request_save (client, flags, error); +} + GKeyFile * gsm_client_save (GsmClient *client, GError **error) { g_return_val_if_fail (GSM_IS_CLIENT (client), FALSE); return GSM_CLIENT_GET_CLASS (client)->impl_save (client, error); } void gsm_client_end_session_response (GsmClient *client, gboolean is_ok, gboolean do_last, gboolean cancel, const char *reason) { g_signal_emit (client, signals[END_SESSION_RESPONSE], 0, is_ok, do_last, cancel, reason); } diff --git a/gnome-session/gsm-client.h b/gnome-session/gsm-client.h index 4ef5dc9..46c913a 100644 --- a/gnome-session/gsm-client.h +++ b/gnome-session/gsm-client.h @@ -63,105 +63,111 @@ struct _GsmClient GObject parent; GsmClientPrivate *priv; }; struct _GsmClientClass { GObjectClass parent_class; /* signals */ void (*disconnected) (GsmClient *client); void (*end_session_response) (GsmClient *client, gboolean ok, gboolean do_last, gboolean cancel, const char *reason); /* virtual methods */ char * (*impl_get_app_name) (GsmClient *client); GsmClientRestartStyle (*impl_get_restart_style_hint) (GsmClient *client); guint (*impl_get_unix_process_id) (GsmClient *client); gboolean (*impl_query_end_session) (GsmClient *client, GsmClientEndSessionFlag flags, GError **error); gboolean (*impl_end_session) (GsmClient *client, GsmClientEndSessionFlag flags, GError **error); gboolean (*impl_cancel_end_session) (GsmClient *client, GError **error); gboolean (*impl_stop) (GsmClient *client, GError **error); + gboolean (*impl_request_save) (GsmClient *client, + guint flags, + GError **error); GKeyFile * (*impl_save) (GsmClient *client, GError **error); }; typedef enum { GSM_CLIENT_ERROR_GENERAL = 0, GSM_CLIENT_ERROR_NOT_REGISTERED, GSM_CLIENT_NUM_ERRORS } GsmClientError; #define GSM_CLIENT_ERROR gsm_client_error_quark () #define GSM_CLIENT_TYPE_ERROR (gsm_client_error_get_type ()) GType gsm_client_error_get_type (void); GQuark gsm_client_error_quark (void); GType gsm_client_get_type (void) G_GNUC_CONST; const char *gsm_client_peek_id (GsmClient *client); const char * gsm_client_peek_startup_id (GsmClient *client); const char * gsm_client_peek_app_id (GsmClient *client); guint gsm_client_peek_restart_style_hint (GsmClient *client); guint gsm_client_peek_status (GsmClient *client); char *gsm_client_get_app_name (GsmClient *client); void gsm_client_set_app_id (GsmClient *client, const char *app_id); void gsm_client_set_status (GsmClient *client, guint status); gboolean gsm_client_end_session (GsmClient *client, guint flags, GError **error); gboolean gsm_client_query_end_session (GsmClient *client, guint flags, GError **error); gboolean gsm_client_cancel_end_session (GsmClient *client, GError **error); void gsm_client_disconnected (GsmClient *client); +gboolean gsm_client_request_save (GsmClient *client, + guint flags, + GError **error); GKeyFile *gsm_client_save (GsmClient *client, GError **error); /* exported to bus */ gboolean gsm_client_stop (GsmClient *client, GError **error); gboolean gsm_client_get_startup_id (GsmClient *client, char **startup_id, GError **error); gboolean gsm_client_get_app_id (GsmClient *client, char **app_id, GError **error); gboolean gsm_client_get_restart_style_hint (GsmClient *client, guint *hint, GError **error); gboolean gsm_client_get_status (GsmClient *client, guint *status, GError **error); gboolean gsm_client_get_unix_process_id (GsmClient *client, guint *pid, GError **error); /* private */ void gsm_client_end_session_response (GsmClient *client, gboolean is_ok, gboolean do_last, gboolean cancel, const char *reason); G_END_DECLS diff --git a/gnome-session/gsm-dbus-client.c b/gnome-session/gsm-dbus-client.c index fdd43d2..16c3df0 100644 --- a/gnome-session/gsm-dbus-client.c +++ b/gnome-session/gsm-dbus-client.c @@ -388,60 +388,73 @@ gsm_dbus_client_set_property (GObject *object, static void gsm_dbus_client_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GsmDBusClient *self; self = GSM_DBUS_CLIENT (object); switch (prop_id) { case PROP_BUS_NAME: g_value_set_string (value, self->priv->bus_name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gsm_dbus_client_finalize (GObject *object) { GsmDBusClient *client = (GsmDBusClient *) object; g_free (client->priv->bus_name); G_OBJECT_CLASS (gsm_dbus_client_parent_class)->finalize (object); } +static gboolean +dbus_client_request_save (GsmClient *client, + guint flags, + GError **error) +{ + g_debug ("GsmDBusClient: sending save request to client with id %s", + gsm_client_peek_id (client)); + + /* FIXME: The protocol does not support this */ + + return FALSE; +} + static GKeyFile * dbus_client_save (GsmClient *client, GError **error) { g_debug ("GsmDBusClient: saving client with id %s", gsm_client_peek_id (client)); /* FIXME: We still don't support client saving for D-Bus * session clients */ return NULL; } static gboolean dbus_client_stop (GsmClient *client, GError **error) { GsmDBusClient *dbus_client = (GsmDBusClient *) client; DBusMessage *message; gboolean ret; ret = FALSE; /* unicast the signal to only the registered bus name */ message = dbus_message_new_signal (gsm_client_peek_id (client), SM_DBUS_CLIENT_PRIVATE_INTERFACE, "Stop"); if (message == NULL) { goto out; } @@ -640,60 +653,61 @@ dbus_client_cancel_end_session (GsmClient *client, return ret; } static void gsm_dbus_client_dispose (GObject *object) { GsmDBusClient *client; g_return_if_fail (object != NULL); g_return_if_fail (GSM_IS_DBUS_CLIENT (object)); client = GSM_DBUS_CLIENT (object); dbus_connection_remove_filter (client->priv->connection, client_dbus_filter_function, client); G_OBJECT_CLASS (gsm_dbus_client_parent_class)->dispose (object); } static void gsm_dbus_client_class_init (GsmDBusClientClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GsmClientClass *client_class = GSM_CLIENT_CLASS (klass); object_class->finalize = gsm_dbus_client_finalize; object_class->constructor = gsm_dbus_client_constructor; object_class->get_property = gsm_dbus_client_get_property; object_class->set_property = gsm_dbus_client_set_property; object_class->dispose = gsm_dbus_client_dispose; + client_class->impl_request_save = dbus_client_request_save; client_class->impl_save = dbus_client_save; client_class->impl_stop = dbus_client_stop; client_class->impl_query_end_session = dbus_client_query_end_session; client_class->impl_end_session = dbus_client_end_session; client_class->impl_cancel_end_session = dbus_client_cancel_end_session; client_class->impl_get_app_name = dbus_client_get_app_name; client_class->impl_get_restart_style_hint = dbus_client_get_restart_style_hint; client_class->impl_get_unix_process_id = dbus_client_get_unix_process_id; g_object_class_install_property (object_class, PROP_BUS_NAME, g_param_spec_string ("bus-name", "bus-name", "bus-name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); g_type_class_add_private (klass, sizeof (GsmDBusClientPrivate)); } GsmClient * gsm_dbus_client_new (const char *startup_id, const char *bus_name) { GsmDBusClient *client; client = g_object_new (GSM_TYPE_DBUS_CLIENT, "startup-id", startup_id, "bus-name", bus_name, NULL); diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c index c32bbe4..67e0c82 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -55,60 +55,61 @@ #include "gsm-xsmp-client.h" #include "gsm-dbus-client.h" #include "gsm-autostart-app.h" #include "gsm-util.h" #include "gsm-icon-names.h" #include "gsm-system.h" #include "gsm-session-save.h" #include "gsm-shell-extensions.h" #include "gsm-fail-whale.h" #define GSM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_MANAGER, GsmManagerPrivate)) /* UUIDs for log messages */ #define GSM_MANAGER_STARTUP_SUCCEEDED_MSGID "0ce153587afa4095832d233c17a88001" #define GSM_MANAGER_UNRECOVERABLE_FAILURE_MSGID "10dd2dc188b54a5e98970f56499d1f73" #define GSM_MANAGER_DBUS_PATH "/org/gnome/SessionManager" #define GSM_MANAGER_DBUS_NAME "org.gnome.SessionManager" /* Probably about the longest amount of time someone could reasonably * want to wait, at least for something happening more than once. * We can get deployed on very slow media though like CDROM devices, * often with complex stacking/compressing filesystems on top, which * is not a recipie for speed. Particularly now that we throw up * a fail whale if required components don't show up quickly enough, * let's make this fairly long. */ #define GSM_MANAGER_PHASE_TIMEOUT 90 /* seconds */ +#define GSM_MANAGER_SAVE_SESSION_TIMEOUT 2 #define GDM_FLEXISERVER_COMMAND "gdmflexiserver" #define GDM_FLEXISERVER_ARGS "--startnew Standard" #define SESSION_SCHEMA "org.gnome.desktop.session" #define KEY_IDLE_DELAY "idle-delay" #define KEY_SESSION_NAME "session-name" #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" #define KEY_AUTOSAVE "auto-save-session" #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" #define KEY_LOGOUT_PROMPT "logout-prompt" #define KEY_SHOW_FALLBACK_WARNING "show-fallback-warning" #define SCREENSAVER_SCHEMA "org.gnome.desktop.screensaver" #define KEY_SLEEP_LOCK "lock-enabled" #define LOCKDOWN_SCHEMA "org.gnome.desktop.lockdown" #define KEY_DISABLE_LOG_OUT "disable-log-out" #define KEY_DISABLE_USER_SWITCHING "disable-user-switching" static void app_registered (GsmApp *app, GsmManager *manager); typedef enum { GSM_MANAGER_LOGOUT_NONE, GSM_MANAGER_LOGOUT_LOGOUT, GSM_MANAGER_LOGOUT_REBOOT, GSM_MANAGER_LOGOUT_REBOOT_INTERACT, GSM_MANAGER_LOGOUT_SHUTDOWN, @@ -1177,60 +1178,123 @@ end_session_or_show_shell_dialog (GsmManager *manager) end_phase (manager); } break; case GSM_MANAGER_LOGOUT_MODE_FORCE: end_phase (manager); break; default: g_assert_not_reached (); break; } } static void query_end_session_complete (GsmManager *manager) { g_debug ("GsmManager: query end session complete"); /* Remove the timeout since this can be called from outside the timer * and we don't want to have it called twice */ if (manager->priv->query_timeout_id > 0) { g_source_remove (manager->priv->query_timeout_id); manager->priv->query_timeout_id = 0; } end_session_or_show_shell_dialog (manager); } +static gboolean +_client_request_save (GsmClient *client, + ClientEndSessionData *data) +{ + gboolean ret; + GError *error; + + error = NULL; + ret = gsm_client_request_save (client, data->flags, &error); + if (ret) { + g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client)); + data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, + client); + } else if (error) { + g_debug ("GsmManager: unable to query client: %s", error->message); + g_error_free (error); + } + + return FALSE; +} + +static gboolean +_client_request_save_helper (const char *id, + GsmClient *client, + ClientEndSessionData *data) +{ + return _client_request_save (client, data); +} + +static void +query_save_session_complete (GsmManager *manager) +{ + GError *error = NULL; + + if (g_slist_length (manager->priv->next_query_clients) > 0) { + ClientEndSessionData data; + + data.manager = manager; + data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; + + g_slist_foreach (manager->priv->next_query_clients, + (GFunc)_client_request_save, + &data); + + g_slist_free (manager->priv->next_query_clients); + manager->priv->next_query_clients = NULL; + + return; + } + + if (manager->priv->query_timeout_id > 0) { + g_source_remove (manager->priv->query_timeout_id); + manager->priv->query_timeout_id = 0; + } + + gsm_session_save (manager->priv->clients, &error); + + if (error) { + g_warning ("Error saving session: %s", error->message); + g_error_free (error); + } +} + static guint32 generate_cookie (void) { guint32 cookie; cookie = (guint32)g_random_int_range (1, G_MAXINT32); return cookie; } static guint32 _generate_unique_cookie (GsmManager *manager) { guint32 cookie; do { cookie = generate_cookie (); } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); return cookie; } static gboolean _on_query_end_session_timeout (GsmManager *manager) { GSList *l; manager->priv->query_timeout_id = 0; g_debug ("GsmManager: query end session timed out"); @@ -1257,60 +1321,75 @@ _on_query_end_session_timeout (GsmManager *manager) bus_name = NULL; } app_id = g_strdup (gsm_client_peek_app_id (l->data)); if (IS_STRING_EMPTY (app_id)) { /* XSMP clients don't give us an app id unless we start them */ g_free (app_id); app_id = gsm_client_get_app_name (l->data); } cookie = _generate_unique_cookie (manager); inhibitor = gsm_inhibitor_new_for_client (gsm_client_peek_id (l->data), app_id, GSM_INHIBITOR_FLAG_LOGOUT, _("Not responding"), bus_name, cookie); g_free (app_id); gsm_store_add (manager->priv->inhibitors, gsm_inhibitor_peek_id (inhibitor), G_OBJECT (inhibitor)); g_object_unref (inhibitor); } g_slist_free (manager->priv->query_clients); manager->priv->query_clients = NULL; query_end_session_complete (manager); return FALSE; } +static gboolean +_on_query_save_session_timeout (GsmManager *manager) +{ + manager->priv->query_timeout_id = 0; + + g_debug ("GsmManager: query to save session timed out"); + + g_slist_free (manager->priv->query_clients); + manager->priv->query_clients = NULL; + + query_save_session_complete (manager); + + return FALSE; +} + static void do_phase_query_end_session (GsmManager *manager) { ClientEndSessionData data; data.manager = manager; data.flags = 0; if (manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE) { data.flags |= GSM_CLIENT_END_SESSION_FLAG_FORCEFUL; } /* We only query if an app is ready to log out, so we don't use * GSM_CLIENT_END_SESSION_FLAG_SAVE here. */ debug_clients (manager); g_debug ("GsmManager: sending query-end-session to clients (logout mode: %s)", manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_NORMAL? "normal" : manager->priv->logout_mode == GSM_MANAGER_LOGOUT_MODE_FORCE? "forceful": "no confirmation"); gsm_store_foreach (manager->priv->clients, (GsmStoreFunc)_client_query_end_session, &data); /* This phase doesn't time out unless logout is forced. Typically, this * separate timer is only used to show UI. */ manager->priv->query_timeout_id = g_timeout_add_seconds (1, (GSourceFunc)_on_query_end_session_timeout, manager); } static void @@ -1972,67 +2051,86 @@ maybe_save_session (GsmManager *manager) return; /* We only allow session saving when session is running or when * logging out */ if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { return; } if (!auto_save_is_enabled (manager)) { return; } error = NULL; gsm_session_save (manager->priv->clients, &error); if (error) { g_warning ("Error saving session: %s", error->message); g_error_free (error); } } static void _handle_client_end_session_response (GsmManager *manager, GsmClient *client, gboolean is_ok, gboolean do_last, gboolean cancel, const char *reason) { - /* just ignore if received outside of shutdown */ - if (manager->priv->phase < GSM_MANAGER_PHASE_QUERY_END_SESSION) { + /* just ignore if we are not yet running */ + if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { return; } g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); + if (manager->priv->phase == GSM_MANAGER_PHASE_RUNNING) { + /* Ignore responses when no requests were sent */ + if (manager->priv->query_clients == NULL) { + return; + } + + manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); + + if (do_last) { + manager->priv->next_query_clients = g_slist_prepend (manager->priv->next_query_clients, + client); + } + + if (manager->priv->query_clients == NULL) { + query_save_session_complete (manager); + } + return; + } + if (cancel) { cancel_end_session (manager); return; } manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); if (! is_ok && manager->priv->logout_mode != GSM_MANAGER_LOGOUT_MODE_FORCE) { guint cookie; GsmInhibitor *inhibitor; char *app_id; const char *bus_name; /* FIXME: do we support updating the reason? */ /* Create JIT inhibit */ if (GSM_IS_DBUS_CLIENT (client)) { bus_name = gsm_dbus_client_get_bus_name (GSM_DBUS_CLIENT (client)); } else { bus_name = NULL; } app_id = g_strdup (gsm_client_peek_app_id (client)); if (IS_STRING_EMPTY (app_id)) { /* XSMP clients don't give us an app id unless we start them */ g_free (app_id); app_id = gsm_client_get_app_name (client); } cookie = _generate_unique_cookie (manager); @@ -2093,84 +2191,97 @@ on_client_end_session_response (GsmClient *client, client, is_ok, do_last, cancel, reason); } static void on_xsmp_client_logout_request (GsmXSMPClient *client, gboolean show_dialog, GsmManager *manager) { GError *error; int logout_mode; if (show_dialog) { logout_mode = GSM_MANAGER_LOGOUT_MODE_NORMAL; } else { logout_mode = GSM_MANAGER_LOGOUT_MODE_NO_CONFIRMATION; } error = NULL; gsm_manager_logout (manager, logout_mode, &error); if (error != NULL) { g_warning ("Unable to logout: %s", error->message); g_error_free (error); } } static void +on_xsmp_client_save_request (GsmXSMPClient *client, + gboolean show_dialog, + GsmManager *manager) +{ + g_debug ("GsmManager: save_request"); + gsm_manager_save_session (manager, NULL); +} + +static void on_store_client_added (GsmStore *store, const char *id, GsmManager *manager) { GsmClient *client; g_debug ("GsmManager: Client added: %s", id); client = (GsmClient *)gsm_store_lookup (store, id); /* a bit hacky */ if (GSM_IS_XSMP_CLIENT (client)) { g_signal_connect (client, "register-request", G_CALLBACK (on_xsmp_client_register_request), manager); g_signal_connect (client, "register-confirmed", G_CALLBACK (on_xsmp_client_register_confirmed), manager); g_signal_connect (client, "logout-request", G_CALLBACK (on_xsmp_client_logout_request), manager); + g_signal_connect (client, + "save-request", + G_CALLBACK (on_xsmp_client_save_request), + manager); } g_signal_connect (client, "end-session-response", G_CALLBACK (on_client_end_session_response), manager); g_signal_emit (manager, signals [CLIENT_ADDED], 0, id); /* FIXME: disconnect signal handler */ } static void on_store_client_removed (GsmStore *store, const char *id, GsmManager *manager) { g_debug ("GsmManager: Client removed: %s", id); g_signal_emit (manager, signals [CLIENT_REMOVED], 0, id); } static void gsm_manager_set_client_store (GsmManager *manager, GsmStore *store) { g_return_if_fail (GSM_IS_MANAGER (manager)); if (store != NULL) { g_object_ref (store); } @@ -3127,60 +3238,95 @@ gsm_manager_reboot (GsmManager *manager, if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { error = g_error_new (GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_NOT_IN_RUNNING, "Reboot interface is only available during the Running phase"); dbus_g_method_return_error (context, error); g_error_free (error); return FALSE; } if (_log_out_is_locked_down (manager)) { error = g_error_new (GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_LOCKED_DOWN, "Logout has been locked down"); dbus_g_method_return_error (context, error); g_error_free (error); return FALSE; } task = g_task_new (manager, manager->priv->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, context); manager->priv->pending_end_session_tasks = g_slist_prepend (manager->priv->pending_end_session_tasks, task); request_reboot (manager); return TRUE; } gboolean +gsm_manager_save_session (GsmManager *manager, + GError **error) +{ + ClientEndSessionData data; + + g_debug ("GsmManager: SaveSession called"); + + g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); + + if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { + g_set_error (error, + GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "SaveSession interface is only available during the Running phase"); + return FALSE; + } + + data.manager = manager; + data.flags = 0; + gsm_store_foreach (manager->priv->clients, + (GsmStoreFunc)_client_request_save_helper, + &data); + + if (manager->priv->query_clients) { + manager->priv->query_timeout_id = g_timeout_add_seconds (GSM_MANAGER_SAVE_SESSION_TIMEOUT, + (GSourceFunc)_on_query_save_session_timeout, + manager); + return TRUE; + } else { + g_debug ("GsmManager: Nothing to save"); + return FALSE; + } +} + +gboolean gsm_manager_can_shutdown (GsmManager *manager, gboolean *shutdown_available, GError **error) { g_debug ("GsmManager: CanShutdown called"); g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); *shutdown_available = !_log_out_is_locked_down (manager) && (gsm_system_can_stop (manager->priv->system) || gsm_system_can_restart (manager->priv->system) || gsm_system_can_suspend (manager->priv->system) || gsm_system_can_hibernate (manager->priv->system)); return TRUE; } gboolean gsm_manager_logout (GsmManager *manager, guint logout_mode, GError **error) { g_debug ("GsmManager: Logout called"); g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { g_set_error (error, GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_NOT_IN_RUNNING, diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h index a0030b9..bc16654 100644 --- a/gnome-session/gsm-manager.h +++ b/gnome-session/gsm-manager.h @@ -137,60 +137,63 @@ void _gsm_manager_set_active_session (GsmManager * /* exported methods */ gboolean gsm_manager_register_client (GsmManager *manager, const char *app_id, const char *client_startup_id, DBusGMethodInvocation *context); gboolean gsm_manager_unregister_client (GsmManager *manager, const char *session_client_id, DBusGMethodInvocation *context); gboolean gsm_manager_inhibit (GsmManager *manager, const char *app_id, guint toplevel_xid, const char *reason, guint flags, DBusGMethodInvocation *context); gboolean gsm_manager_uninhibit (GsmManager *manager, guint inhibit_cookie, DBusGMethodInvocation *context); gboolean gsm_manager_is_inhibited (GsmManager *manager, guint flags, gboolean *is_inhibited, GError *error); gboolean gsm_manager_shutdown (GsmManager *manager, DBusGMethodInvocation *context); gboolean gsm_manager_reboot (GsmManager *manager, DBusGMethodInvocation *context); +gboolean gsm_manager_save_session (GsmManager *manager, + GError **error); + gboolean gsm_manager_can_shutdown (GsmManager *manager, gboolean *shutdown_available, GError **error); gboolean gsm_manager_logout (GsmManager *manager, guint logout_mode, GError **error); gboolean gsm_manager_setenv (GsmManager *manager, const char *variable, const char *value, GError **error); gboolean gsm_manager_get_locale (GsmManager *manager, int category, const char **value, GError **error); gboolean gsm_manager_initialization_error (GsmManager *manager, const char *message, gboolean fatal, GError **error); gboolean gsm_manager_get_clients (GsmManager *manager, GPtrArray **clients, GError **error); gboolean gsm_manager_get_inhibitors (GsmManager *manager, GPtrArray **inhibitors, GError **error); gboolean gsm_manager_is_autostart_condition_handled (GsmManager *manager, const char *condition, gboolean *handled, GError **error); diff --git a/gnome-session/gsm-xsmp-client.c b/gnome-session/gsm-xsmp-client.c index c256acb..1cfb47f 100644 --- a/gnome-session/gsm-xsmp-client.c +++ b/gnome-session/gsm-xsmp-client.c @@ -39,60 +39,61 @@ #define GsmDesktopFile "_GSM_DesktopFile" #define GSM_XSMP_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientPrivate)) struct GsmXSMPClientPrivate { SmsConn conn; IceConn ice_connection; guint watch_id; char *description; GPtrArray *props; /* SaveYourself state */ int current_save_yourself; int next_save_yourself; guint next_save_yourself_allow_interact : 1; }; enum { PROP_0, PROP_ICE_CONNECTION }; enum { REGISTER_REQUEST, REGISTER_CONFIRMED, LOGOUT_REQUEST, + SAVE_REQUEST, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; G_DEFINE_TYPE (GsmXSMPClient, gsm_xsmp_client, GSM_TYPE_CLIENT) static gboolean client_iochannel_watch (GIOChannel *channel, GIOCondition condition, GsmXSMPClient *client) { gboolean keep_going; g_object_ref (client); switch (IceProcessMessages (client->priv->ice_connection, NULL, NULL)) { case IceProcessMessagesSuccess: keep_going = TRUE; break; case IceProcessMessagesIOError: g_debug ("GsmXSMPClient: IceProcessMessagesIOError on '%s'", client->priv->description); gsm_client_set_status (GSM_CLIENT (client), GSM_CLIENT_FAILED); /* Emitting "disconnected" will eventually cause * IceCloseConnection() to be called. */ gsm_client_disconnected (GSM_CLIENT (client)); keep_going = FALSE; break; @@ -472,60 +473,84 @@ xsmp_interact (GsmClient *client) SmsInteract (xsmp->priv->conn); } static gboolean xsmp_cancel_end_session (GsmClient *client, GError **error) { GsmXSMPClient *xsmp = (GsmXSMPClient *) client; g_debug ("GsmXSMPClient: xsmp_cancel_end_session ('%s')", xsmp->priv->description); if (xsmp->priv->conn == NULL) { g_set_error (error, GSM_CLIENT_ERROR, GSM_CLIENT_ERROR_NOT_REGISTERED, "Client is not registered"); return FALSE; } SmsShutdownCancelled (xsmp->priv->conn); /* reset the state */ xsmp->priv->current_save_yourself = -1; xsmp->priv->next_save_yourself = -1; xsmp->priv->next_save_yourself_allow_interact = FALSE; return TRUE; } +static gboolean +xsmp_request_save (GsmClient *client, + guint flags, + GError **error) +{ + GsmXSMPClient *xsmp = (GsmXSMPClient *) client; + + g_debug ("GsmXSMPClient: xsmp_request_save ('%s')", xsmp->priv->description); + + if (xsmp->priv->conn == NULL) { + g_set_error (error, + GSM_CLIENT_ERROR, + GSM_CLIENT_ERROR_NOT_REGISTERED, + "Client is not registered"); + return FALSE; + } + + if (flags & GSM_CLIENT_END_SESSION_FLAG_LAST) + xsmp_save_yourself_phase2 (client); + else + do_save_yourself (xsmp, SmSaveLocal, FALSE); + + return TRUE; +} static char * get_desktop_file_path (GsmXSMPClient *client) { SmProp *prop; char *desktop_file_path = NULL; const char *program_name; /* XSMP clients using eggsmclient defines a special property * pointing to their respective desktop entry file */ prop = find_property (client, GsmDesktopFile, NULL); if (prop) { GFile *file = g_file_new_for_uri (prop->vals[0].value); desktop_file_path = g_file_get_path (file); g_object_unref (file); goto out; } /* If we can't get desktop file from GsmDesktopFile then we * try to find the desktop file from its program name */ prop = find_property (client, SmProgram, NULL); if (!prop) { goto out; } program_name = prop->vals[0].value; desktop_file_path = gsm_util_find_desktop_file_for_app_name (program_name, TRUE, FALSE); @@ -955,100 +980,112 @@ xsmp_get_unix_process_id (GsmClient *client) gboolean res; g_debug ("GsmXSMPClient: getting pid"); prop = find_property (GSM_XSMP_CLIENT (client), SmProcessID, NULL); if (!prop || strcmp (prop->type, SmARRAY8) != 0) { return 0; } pid = 0; res = _parse_value_as_uint ((char *)prop->vals[0].value, &pid); if (! res) { pid = 0; } return pid; } static void gsm_xsmp_client_class_init (GsmXSMPClientClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GsmClientClass *client_class = GSM_CLIENT_CLASS (klass); object_class->finalize = gsm_xsmp_client_finalize; object_class->constructor = gsm_xsmp_client_constructor; object_class->get_property = gsm_xsmp_client_get_property; object_class->set_property = gsm_xsmp_client_set_property; + client_class->impl_request_save = xsmp_request_save; client_class->impl_save = xsmp_save; client_class->impl_stop = xsmp_stop; client_class->impl_query_end_session = xsmp_query_end_session; client_class->impl_end_session = xsmp_end_session; client_class->impl_cancel_end_session = xsmp_cancel_end_session; client_class->impl_get_app_name = xsmp_get_app_name; client_class->impl_get_restart_style_hint = xsmp_get_restart_style_hint; client_class->impl_get_unix_process_id = xsmp_get_unix_process_id; signals[REGISTER_REQUEST] = g_signal_new ("register-request", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsmXSMPClientClass, register_request), _boolean_handled_accumulator, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_POINTER); signals[REGISTER_CONFIRMED] = g_signal_new ("register-confirmed", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsmXSMPClientClass, register_confirmed), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); signals[LOGOUT_REQUEST] = g_signal_new ("logout-request", G_OBJECT_CLASS_TYPE (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GsmXSMPClientClass, logout_request), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + signals[SAVE_REQUEST] = + g_signal_new ("save-request", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmXSMPClientClass, save_request), + NULL, + NULL, + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, + 1, G_TYPE_BOOLEAN); + g_object_class_install_property (object_class, PROP_ICE_CONNECTION, g_param_spec_pointer ("ice-connection", "ice-connection", "ice-connection", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (GsmXSMPClientPrivate)); } GsmClient * gsm_xsmp_client_new (IceConn ice_conn) { GsmXSMPClient *xsmp; xsmp = g_object_new (GSM_TYPE_XSMP_CLIENT, "ice-connection", ice_conn, NULL); return GSM_CLIENT (xsmp); } static Status register_client_callback (SmsConn conn, SmPointer manager_data, char *previous_id) { GsmXSMPClient *client = manager_data; gboolean handled; char *id; diff --git a/gnome-session/gsm-xsmp-client.h b/gnome-session/gsm-xsmp-client.h index 1bb2797..6b95c51 100644 --- a/gnome-session/gsm-xsmp-client.h +++ b/gnome-session/gsm-xsmp-client.h @@ -27,61 +27,62 @@ G_BEGIN_DECLS #define GSM_TYPE_XSMP_CLIENT (gsm_xsmp_client_get_type ()) #define GSM_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClient)) #define GSM_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) #define GSM_IS_XSMP_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_XSMP_CLIENT)) #define GSM_IS_XSMP_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_XSMP_CLIENT)) #define GSM_XSMP_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSM_TYPE_XSMP_CLIENT, GsmXSMPClientClass)) typedef struct _GsmXSMPClient GsmXSMPClient; typedef struct _GsmXSMPClientClass GsmXSMPClientClass; typedef struct GsmXSMPClientPrivate GsmXSMPClientPrivate; struct _GsmXSMPClient { GsmClient parent; GsmXSMPClientPrivate *priv; }; struct _GsmXSMPClientClass { GsmClientClass parent_class; /* signals */ gboolean (*register_request) (GsmXSMPClient *client, char **client_id); void (*register_confirmed) (GsmXSMPClient *client, const char *client_id); gboolean (*logout_request) (GsmXSMPClient *client, gboolean prompt); - + gboolean (*save_request) (GsmXSMPClient *client, + gboolean prompt); void (*saved_state) (GsmXSMPClient *client); void (*request_phase2) (GsmXSMPClient *client); void (*request_interaction) (GsmXSMPClient *client); void (*interaction_done) (GsmXSMPClient *client, gboolean cancel_shutdown); void (*save_yourself_done) (GsmXSMPClient *client); }; GType gsm_xsmp_client_get_type (void) G_GNUC_CONST; GsmClient *gsm_xsmp_client_new (IceConn ice_conn); void gsm_xsmp_client_connect (GsmXSMPClient *client, SmsConn conn, unsigned long *mask_ret, SmsCallbacks *callbacks_ret); void gsm_xsmp_client_save_state (GsmXSMPClient *client); void gsm_xsmp_client_save_yourself (GsmXSMPClient *client, gboolean save_state); void gsm_xsmp_client_save_yourself_phase2 (GsmXSMPClient *client); void gsm_xsmp_client_interact (GsmXSMPClient *client); void gsm_xsmp_client_shutdown_cancelled (GsmXSMPClient *client); G_END_DECLS diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml index 8c31694..eacac4b 100644 --- a/gnome-session/org.gnome.SessionManager.xml +++ b/gnome-session/org.gnome.SessionManager.xml @@ -261,60 +261,68 @@ True if condition is handled, false otherwise Allows the caller to determine whether the session manager is handling changes to the specified autostart condition. Request a shutdown dialog. Request a reboot dialog. + + + + Request to save session + + + + True if shutdown is available to the user, false otherwise Allows the caller to determine whether or not it's okay to show a shutdown option in the UI The type of logout that is being requested Request a logout dialog Allowed values for the mode parameter are: 0 Normal. -- 2.3.7 From 7b1c1782ef895a536f95d83e0d6596dd076ee32f Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:22:07 -0500 Subject: [PATCH 09/17] Revert "Allow saved-session to be a symlink" This reverts commit b733c2ee519b65c3c4eab0d0e93056412f995f3f. --- gnome-session/gsm-session-save.c | 32 ++++++++++++++++++++++++++++---- gnome-session/gsm-util.c | 6 ++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c index 025eeea..131a58b 100644 --- a/gnome-session/gsm-session-save.c +++ b/gnome-session/gsm-session-save.c @@ -9,61 +9,61 @@ * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include "gsm-util.h" #include "gsm-autostart-app.h" #include "gsm-client.h" #include "gsm-session-save.h" #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" static gboolean gsm_session_clear_saved_session (const char *directory, GHashTable *discard_hash); typedef struct { - const char *dir; + char *dir; GHashTable *discard_hash; GError **error; } SessionSaveData; static gboolean save_one_client (char *id, GObject *object, SessionSaveData *data) { GsmClient *client; GKeyFile *keyfile; const char *app_id; char *path = NULL; char *filename = NULL; char *contents = NULL; gsize length = 0; char *discard_exec; GError *local_error; client = GSM_CLIENT (object); local_error = NULL; keyfile = gsm_client_save (client, &local_error); if (keyfile == NULL || local_error) { goto out; } contents = g_key_file_to_data (keyfile, &length, &local_error); @@ -112,89 +112,113 @@ save_one_client (char *id, } g_debug ("GsmSessionSave: saved client %s to %s", id, filename); out: if (keyfile != NULL) { g_key_file_free (keyfile); } g_free (contents); g_free (filename); g_free (path); /* in case of any error, stop saving session */ if (local_error) { g_propagate_error (data->error, local_error); g_error_free (local_error); return TRUE; } return FALSE; } void gsm_session_save (GsmStore *client_store, GError **error) { GSettings *settings; const char *save_dir; + char *tmp_dir; SessionSaveData data; g_debug ("GsmSessionSave: Saving session"); /* Clear one shot key autosave in the event its set (so that it's actually * one shot only) */ settings = g_settings_new (GSM_MANAGER_SCHEMA); g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE); g_object_unref (settings); save_dir = gsm_util_get_saved_session_dir (); if (save_dir == NULL) { g_warning ("GsmSessionSave: cannot create saved session directory"); return; } - data.dir = save_dir; + tmp_dir = gsm_util_get_empty_tmp_session_dir (); + if (tmp_dir == NULL) { + g_warning ("GsmSessionSave: cannot create new saved session directory"); + return; + } + + /* save the session in a temp directory, and remember the discard + * commands */ + data.dir = tmp_dir; data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - /* remove old saved session */ - gsm_session_clear_saved_session (save_dir, data.discard_hash); data.error = error; gsm_store_foreach (client_store, (GsmStoreFunc) save_one_client, &data); + if (!*error) { + /* remove the old saved session */ + gsm_session_clear_saved_session (save_dir, data.discard_hash); + + /* rename the temp session dir */ + if (g_file_test (save_dir, G_FILE_TEST_IS_DIR)) + g_rmdir (save_dir); + g_rename (tmp_dir, save_dir); + } else { + g_warning ("GsmSessionSave: error saving session: %s", (*error)->message); + /* FIXME: we should create a hash table filled with the discard + * commands that are in desktop files from save_dir. */ + gsm_session_clear_saved_session (tmp_dir, NULL); + g_rmdir (tmp_dir); + } + g_hash_table_destroy (data.discard_hash); + g_free (tmp_dir); } static gboolean gsm_session_clear_one_client (const char *filename, GHashTable *discard_hash) { gboolean result = TRUE; GKeyFile *key_file; char *discard_exec = NULL; g_debug ("GsmSessionSave: removing '%s' from saved session", filename); key_file = g_key_file_new (); if (g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL)) { char **argv; int argc; discard_exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, GSM_AUTOSTART_APP_DISCARD_KEY, NULL); if (!discard_exec) goto out; if (discard_hash && g_hash_table_lookup (discard_hash, discard_exec)) goto out; if (!g_shell_parse_argv (discard_exec, &argc, &argv, NULL)) goto out; diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c index ff6484c..bf24830 100644 --- a/gnome-session/gsm-util.c +++ b/gnome-session/gsm-util.c @@ -71,63 +71,69 @@ gsm_util_find_desktop_file_for_app_name (const char *name, g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); } /* look for gnome vendor prefix */ if (app_path == NULL) { g_free (desktop_file); desktop_file = g_strdup_printf ("gnome-%s.desktop", name); g_key_file_load_from_dirs (key_file, desktop_file, (const char **) app_dirs, &app_path, G_KEY_FILE_NONE, NULL); if (app_path != NULL) { g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); } } g_free (desktop_file); g_key_file_free (key_file); g_strfreev (app_dirs); return app_path; } static gboolean ensure_dir_exists (const char *dir) { + if (g_file_test (dir, G_FILE_TEST_IS_DIR)) + return TRUE; + if (g_mkdir_with_parents (dir, 0755) == 0) return TRUE; + if (errno == EEXIST) + return g_file_test (dir, G_FILE_TEST_IS_DIR); + g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno)); return FALSE; } gchar * gsm_util_get_empty_tmp_session_dir (void) { char *tmp; gboolean exists; tmp = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session.new", NULL); exists = ensure_dir_exists (tmp); if (G_UNLIKELY (!exists)) { g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp); g_free (tmp); return NULL; } else { /* make sure it's empty */ GDir *dir; const char *filename; dir = g_dir_open (tmp, 0, NULL); if (dir) { while ((filename = g_dir_read_name (dir))) { -- 2.3.7 From efdb8db827399dadd3b8638b201e7da81f6e9592 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:22:53 -0500 Subject: [PATCH 10/17] Allow saved-session directory to be a symlink This gives us the option of adding a rudimentary session chooser later. --- gnome-session/gsm-session-save.c | 36 ++++++++++++++++++++++++++++++------ gnome-session/gsm-util.c | 6 ------ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c index 131a58b..ea23be5 100644 --- a/gnome-session/gsm-session-save.c +++ b/gnome-session/gsm-session-save.c @@ -148,67 +148,91 @@ gsm_session_save (GsmStore *client_store, * one shot only) */ settings = g_settings_new (GSM_MANAGER_SCHEMA); g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE); g_object_unref (settings); save_dir = gsm_util_get_saved_session_dir (); if (save_dir == NULL) { g_warning ("GsmSessionSave: cannot create saved session directory"); return; } tmp_dir = gsm_util_get_empty_tmp_session_dir (); if (tmp_dir == NULL) { g_warning ("GsmSessionSave: cannot create new saved session directory"); return; } /* save the session in a temp directory, and remember the discard * commands */ data.dir = tmp_dir; data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); data.error = error; gsm_store_foreach (client_store, (GsmStoreFunc) save_one_client, &data); if (!*error) { - /* remove the old saved session */ - gsm_session_clear_saved_session (save_dir, data.discard_hash); + char *session_dir; - /* rename the temp session dir */ - if (g_file_test (save_dir, G_FILE_TEST_IS_DIR)) - g_rmdir (save_dir); - g_rename (tmp_dir, save_dir); + if (g_file_test (save_dir, G_FILE_TEST_IS_SYMLINK)) + session_dir = g_file_read_link (save_dir, error); + else + session_dir = g_strdup (save_dir); + + if (session_dir != NULL) { + + char *absolute_session_dir; + + if (g_path_is_absolute (session_dir)) { + absolute_session_dir = g_strdup (session_dir); + } else { + char *parent_dir; + + parent_dir = g_path_get_dirname (save_dir); + absolute_session_dir = g_build_filename (parent_dir, session_dir, NULL); + g_free (parent_dir); + } + g_free (session_dir); + + /* remove the old saved session */ + gsm_session_clear_saved_session (absolute_session_dir, data.discard_hash); + + if (g_file_test (absolute_session_dir, G_FILE_TEST_IS_DIR)) + g_rmdir (absolute_session_dir); + g_rename (tmp_dir, absolute_session_dir); + + g_free (absolute_session_dir); + } } else { g_warning ("GsmSessionSave: error saving session: %s", (*error)->message); /* FIXME: we should create a hash table filled with the discard * commands that are in desktop files from save_dir. */ gsm_session_clear_saved_session (tmp_dir, NULL); g_rmdir (tmp_dir); } g_hash_table_destroy (data.discard_hash); g_free (tmp_dir); } static gboolean gsm_session_clear_one_client (const char *filename, GHashTable *discard_hash) { gboolean result = TRUE; GKeyFile *key_file; char *discard_exec = NULL; g_debug ("GsmSessionSave: removing '%s' from saved session", filename); key_file = g_key_file_new (); if (g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL)) { char **argv; int argc; discard_exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, diff --git a/gnome-session/gsm-util.c b/gnome-session/gsm-util.c index bf24830..ff6484c 100644 --- a/gnome-session/gsm-util.c +++ b/gnome-session/gsm-util.c @@ -71,69 +71,63 @@ gsm_util_find_desktop_file_for_app_name (const char *name, g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); } /* look for gnome vendor prefix */ if (app_path == NULL) { g_free (desktop_file); desktop_file = g_strdup_printf ("gnome-%s.desktop", name); g_key_file_load_from_dirs (key_file, desktop_file, (const char **) app_dirs, &app_path, G_KEY_FILE_NONE, NULL); if (app_path != NULL) { g_debug ("GsmUtil: found in XDG dirs: '%s'", app_path); } } g_free (desktop_file); g_key_file_free (key_file); g_strfreev (app_dirs); return app_path; } static gboolean ensure_dir_exists (const char *dir) { - if (g_file_test (dir, G_FILE_TEST_IS_DIR)) - return TRUE; - if (g_mkdir_with_parents (dir, 0755) == 0) return TRUE; - if (errno == EEXIST) - return g_file_test (dir, G_FILE_TEST_IS_DIR); - g_warning ("GsmSessionSave: Failed to create directory %s: %s", dir, strerror (errno)); return FALSE; } gchar * gsm_util_get_empty_tmp_session_dir (void) { char *tmp; gboolean exists; tmp = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session.new", NULL); exists = ensure_dir_exists (tmp); if (G_UNLIKELY (!exists)) { g_warning ("GsmSessionSave: could not create directory for saved session: %s", tmp); g_free (tmp); return NULL; } else { /* make sure it's empty */ GDir *dir; const char *filename; dir = g_dir_open (tmp, 0, NULL); if (dir) { while ((filename = g_dir_read_name (dir))) { -- 2.3.7 From fdc43e0cc51bf0d16bb9173bd3a23cf27c1ed2b3 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:28:53 -0500 Subject: [PATCH 11/17] Tie session selector to properties dialog --- capplet/gsm-properties-dialog.c | 30 +++++- configure.ac | 3 +- data/session-selector.ui | 2 +- tools/Makefile.am | 1 + tools/gnome-session-selector.c | 211 ++++++++++++++++++++++++++++++++-------- 5 files changed, 200 insertions(+), 47 deletions(-) diff --git a/capplet/gsm-properties-dialog.c b/capplet/gsm-properties-dialog.c index d2be778..51fa510 100644 --- a/capplet/gsm-properties-dialog.c +++ b/capplet/gsm-properties-dialog.c @@ -471,88 +471,114 @@ session_saved_message (GsmPropertiesDialog *dialog, { GtkLabel *label; gchar *markup; label = GTK_LABEL (gtk_builder_get_object (dialog->priv->xml, CAPPLET_SESSION_SAVED_WIDGET_NAME)); if (is_error) markup = g_markup_printf_escaped ("%s", msg); else markup = g_markup_escape_text (msg, -1); gtk_label_set_markup (label, markup); g_free (markup); } static void session_saved_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, void *user_data) { gboolean res; GsmPropertiesDialog *dialog = user_data; res = dbus_g_proxy_end_call (proxy, call_id, NULL, G_TYPE_INVALID); if (res) session_saved_message (dialog, _("Your session has been saved."), FALSE); else session_saved_message (dialog, _("Failed to save session"), TRUE); g_object_unref (proxy); } static void -on_save_session_clicked (GtkWidget *widget, - GsmPropertiesDialog *dialog) +save_session_directly (GsmPropertiesDialog *dialog) { DBusGConnection *conn; DBusGProxy *proxy; DBusGProxyCall *call; conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); if (conn == NULL) { session_saved_message (dialog, _("Could not connect to the session bus"), TRUE); return; } proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); if (proxy == NULL) { session_saved_message (dialog, _("Could not connect to the session manager"), TRUE); return; } call = dbus_g_proxy_begin_call (proxy, "SaveSession", session_saved_cb, dialog, NULL, G_TYPE_INVALID); if (call == NULL) { session_saved_message (dialog, _("Failed to save session"), TRUE); g_object_unref (proxy); return; } } static void +save_session_from_selector (GsmPropertiesDialog *dialog, + const char *program_path) +{ + char *command_line = g_strdup_printf ("%s --action save", program_path); + + g_spawn_command_line_sync (command_line, NULL, NULL, NULL, NULL); + + g_free (command_line); +} + +static void +on_save_session_clicked (GtkWidget *widget, + GsmPropertiesDialog *dialog) +{ + char *program_path; + + program_path = g_find_program_in_path ("gnome-session-selector"); + + if (program_path != NULL) { + save_session_from_selector (dialog, program_path); + g_free (program_path); + } else { + save_session_directly (dialog); + } +} + +static void setup_dialog (GsmPropertiesDialog *dialog) { GtkTreeView *treeview; GtkWidget *button; GtkTreeModel *tree_filter; GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeSelection *selection; GtkTargetList *targetlist; gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); dialog->priv->list_store = gtk_list_store_new (NUMBER_OF_COLUMNS, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_ICON, G_TYPE_STRING, G_TYPE_OBJECT, G_TYPE_STRING); tree_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->list_store), NULL); g_object_unref (dialog->priv->list_store); dialog->priv->tree_filter = tree_filter; gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (tree_filter), STORE_COL_VISIBLE); treeview = GTK_TREE_VIEW (gtk_builder_get_object (dialog->priv->xml, diff --git a/configure.ac b/configure.ac index 505292c..9b97c15 100644 --- a/configure.ac +++ b/configure.ac @@ -21,92 +21,91 @@ LT_PREREQ([2.2.6]) LT_INIT([dlopen disable-static]) GNOME_MAINTAINER_MODE_DEFINES GNOME_COMPILE_WARNINGS([maximum]) AC_ARG_ENABLE(deprecation_flags, [AS_HELP_STRING([--enable-deprecation-flags], [use *_DISABLE_DEPRECATED flags @<:@default=no@:>@])],, [enable_deprecation_flags=no]) if test "x$enable_deprecation_flags" = "xyes"; then DISABLE_DEPRECATED_CFLAGS=$DISABLE_DEPRECATED AC_SUBST([DISABLE_DEPRECATED_CFLAGS]) fi GLIB_REQUIRED=2.35.0 GTK3_REQUIRED=2.90.7 DBUS_GLIB_REQUIRED=0.76 UPOWER_REQUIRED=0.9.0 JSON_GLIB_REQUIRED=0.10 GNOME_DESKTOP_REQUIRED=3.9.91 AC_ARG_ENABLE(session-selector, AS_HELP_STRING([--enable-session-selector], [enable building a custom session selector dialog]), enable_session_selector=$enableval,enable_session_selector=no) AM_CONDITIONAL(BUILD_SESSION_SELECTOR, [test "$enable_session_selector" = yes]) if test "$enable_session_selector" = yes; then - PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0) + PKG_CHECK_MODULES(SESSION_SELECTOR, gtk+-3.0 gio-2.0 dbus-glib-1 >= $DBUS_GLIB_REQUIRED) fi dnl ==================================================================== dnl Dependency Checks dnl ==================================================================== dnl Standard vertical stacks PKG_CHECK_MODULES(GIO, gio-2.0) PKG_CHECK_MODULES(GIOUNIX, gio-unix-2.0 >= $GLIB_REQUIRED) PKG_CHECK_MODULES(GTK3, gtk+-3.0 >= $GTK3_REQUIRED) PKG_CHECK_MODULES(GNOME_SESSION, glib-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED dbus-glib-1 >= $DBUS_GLIB_REQUIRED json-glib-1.0 >= $JSON_GLIB_REQUIRED gnome-desktop-3.0 >= $GNOME_DESKTOP_REQUIRED ) dnl We can only support old upower dnl https://bugzilla.gnome.org/show_bug.cgi?id=710383 PKG_CHECK_MODULES(UPOWER, upower-glib < 0.99.0, have_old_upower=yes, have_old_upower=no) AS_IF([test x$have_old_upower = xyes], [ AC_DEFINE([HAVE_OLD_UPOWER], [1], [Define if we have an older upower]) ]) AM_CONDITIONAL(HAVE_OLD_UPOWER, test x$have_old_upower = xyes) PKG_CHECK_MODULES(SESSION_PROPERTIES, glib-2.0 >= $GLIB_REQUIRED gtk+-3.0 >= $GTK3_REQUIRED - dbus-glib-1 >= $DBUS_GLIB_REQUIRED ) PKG_CHECK_MODULES(X11, x11) PKG_CHECK_MODULES(SM, sm) PKG_CHECK_MODULES(ICE, ice) PKG_CHECK_MODULES(XEXT, xext xau) PKG_CHECK_MODULES(DBUS_GLIB, dbus-glib-1 >= $DBUS_GLIB_REQUIRED) PKG_CHECK_MODULES(EGG_SMCLIENT, gtk+-3.0) PKG_CHECK_MODULES(GL_TEST, xcomposite gl glib-2.0) dnl ==================================================================== dnl Check for gconf dnl ==================================================================== AC_ARG_ENABLE([gconf], AS_HELP_STRING([--enable-gconf], [Support gconf-based autostart]), [enable_gconf=$enableval], [enable_gconf=auto]) PKG_CHECK_MODULES(GCONF, gconf-2.0, [have_gconf=yes], [have_gconf=no]) if test x$enable_gconf = xauto ; then enable_gconf=$have_gconf elif test x$enable_gconf = xyes -a x$have_gconf = xno ; then AC_MSG_ERROR([GConf support explicitly required, but gconf not found]) fi if test x$enable_gconf = xyes ; then diff --git a/data/session-selector.ui b/data/session-selector.ui index 1c55712..1534a74 100644 --- a/data/session-selector.ui +++ b/data/session-selector.ui @@ -20,61 +20,61 @@ True 0.5 out True 12 True vertical 6 True other True vertical 0 True 0.0 0.5 - Please select a custom session to run + Please select a custom session to use True True 0 False True 0 True vertical 12 True 12 True True never diff --git a/tools/Makefile.am b/tools/Makefile.am index 57b82c7..715affe 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -49,37 +49,38 @@ gnome_session_check_accelerated_helper_CPPFLAGS = \ -DPKGDATADIR=\""$(pkgdatadir)"\" \ $(GL_TEST_CFLAGS) gnome_session_check_accelerated_helper_LDADD = \ $(GL_TEST_LIBS) \ $(X11_LIBS) gnome_session_check_accelerated_SOURCES = \ gnome-session-check-accelerated-common.h \ gnome-session-check-accelerated.c gnome_session_check_accelerated_CPPFLAGS = \ -DLIBEXECDIR=\""$(libexecdir)"\" \ $(AM_CPPFLAGS) \ $(GTK3_CFLAGS) gnome_session_check_accelerated_LDADD = \ $(GTK3_LIBS) \ $(X11_LIBS) if BUILD_SESSION_SELECTOR gnome_session_selector_CPPFLAGS = \ $(AM_CPPFLAGS) \ $(GNOME_SESSION_CFLAGS) \ $(DBUS_GLIB_CFLAGS) \ -DGTKBUILDER_DIR=\""$(pkgdatadir)"\" \ -DLOCALE_DIR=\""$(datadir)/locale"\" \ $(DISABLE_DEPRECATED_CFLAGS) gnome_session_selector_LDADD = \ + $(DBUS_GLIB_CFLAGS) \ $(SESSION_SELECTOR_LIBS) gnome_session_selector_SOURCES = \ gnome-session-selector.c endif -include $(top_srcdir)/git.mk diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c index d7584ff..8f37eca 100644 --- a/tools/gnome-session-selector.c +++ b/tools/gnome-session-selector.c @@ -7,126 +7,133 @@ * 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, see . * * Written by: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include +#include +#include + +#define GSM_SERVICE_DBUS "org.gnome.SessionManager" +#define GSM_PATH_DBUS "/org/gnome/SessionManager" +#define GSM_INTERFACE_DBUS "org.gnome.SessionManager" + #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" static GtkBuilder *builder; static GtkWidget *session_list; static GtkListStore *store; static GtkTreeModelSort *sort_model; +static char *info_text; static void select_session (const char *name); +static gboolean make_session_current (const char *name); static char * get_session_path (const char *name) { return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); } static char * find_new_session_name (void) { char *name; char *path; int i; for (i = 1; i < 20; i++) { name = g_strdup_printf (_("Session %d"), i); path = get_session_path (name); if (!g_file_test (path, G_FILE_TEST_EXISTS)) { g_free (path); return name; } g_free (path); g_free (name); } return NULL; } static gboolean is_valid_session_name (const char *name) { GtkTreeIter iter; char *n; - const char *info_text; char *warning_text; gboolean user_tried_dot; gboolean user_tried_slash; GtkWidget *info_bar; GtkWidget *label; if (name[0] == 0) { return FALSE; } if (name[0] == '.') { user_tried_dot = TRUE; } else { user_tried_dot = FALSE; } if (strchr (name, '/') != NULL) { user_tried_slash = TRUE; } else { user_tried_slash = FALSE; } - info_text = _("Please select a custom session to run"); warning_text = NULL; if (user_tried_dot && user_tried_slash) { warning_text = g_strdup_printf ("%s\nNote: %s", info_text, _("Session names are not allowed to start with ā€˜.ā€™ or contain ā€˜/ā€™ characters")); } else if (user_tried_dot) { warning_text = g_strdup_printf ("%s\nNote: %s", info_text, _("Session names are not allowed to start with ā€˜.ā€™")); } else if (user_tried_slash) { warning_text = g_strdup_printf ("%s\nNote: %s", info_text, _("Session names are not allowed to contain ā€˜/ā€™ characters")); } gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); do { gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &n, -1); if (strcmp (n, name) == 0) { char *message; message = g_strdup_printf (_("A session named ā€˜%sā€™ already exists"), name); warning_text = g_strdup_printf ("%s\nNote: %s", info_text, message); g_free (message); g_free (n); break; } g_free (n); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); info_bar = (GtkWidget *) gtk_builder_get_object (builder, "info-bar"); @@ -182,517 +189,637 @@ populate_session_list (GtkWidget *session_list) default_name = NULL; if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { default_name = g_path_get_basename (last_session); } while ((name = g_dir_read_name (dir)) != NULL) { if (strcmp (name, "saved-session") == 0) continue; gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); if (g_strcmp0 (default_name, name) == 0) { GtkTreeSelection *selection; GtkTreeIter child_iter; gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &child_iter, &iter); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); gtk_tree_selection_select_iter (selection, &child_iter); } } g_free (default_name); g_dir_close (dir); out: g_free (saved_session); g_free (path); } static char * +get_last_session (void) +{ + char *saved_session; + char last_session[PATH_MAX] = ""; + char *name = NULL; + + saved_session = get_session_path ("saved-session"); + + if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { + name = g_path_get_basename (last_session); + } + + g_free (saved_session); + + return name; +} + +static char * get_selected_session (void) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; gchar *name; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { gtk_tree_model_get (model, &iter, 0, &name, -1); return name; } return NULL; } static void remove_session (const char *name) { char *path1, *path2; char *n, *path; const char *d; GDir *dir; GError *error; path1 = get_session_path ("saved-session"); path2 = get_session_path (name); error = NULL; n = g_file_read_link (path1, &error); if (n == NULL) { g_warning ("Failed to read link: %s", error->message); g_error_free (error); } else if (strcmp (n, name) == 0) { unlink (path1); } g_free (n); dir = g_dir_open (path2, 0, NULL); while ((d = g_dir_read_name (dir)) != NULL) { path = g_build_filename (path2, d, NULL); unlink (path); g_free (path); } g_dir_close (dir); remove (path2); g_free (path1); g_free (path2); } +static gboolean +make_session_current (const char *name) +{ + char *path1; + gboolean ret = TRUE; + + path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL); + + unlink (path1); + if (symlink (name, path1) < 0) { + g_warning ("Failed to make session '%s' current", name); + ret = FALSE; + } + + g_free (path1); + + return ret; +} + static void on_remove_session_clicked (GtkButton *button, gpointer data) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; char *name; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { GtkTreeIter child_iter; gtk_tree_model_get (model, &iter, 0, &name, -1); remove_session (name); g_free (name); gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model), &child_iter, &iter); gtk_list_store_remove (GTK_LIST_STORE (store), &child_iter); if (!gtk_tree_selection_get_selected (selection, NULL, NULL)) { gtk_tree_model_get_iter_first (model, &iter); gtk_tree_model_get (model, &iter, 0, &name, -1); select_session (name); + make_session_current (name); g_free (name); } } } static void begin_rename (void) { GtkTreePath *path; GtkTreeViewColumn *column; GList *cells; gtk_widget_grab_focus (session_list); gtk_tree_view_get_cursor (GTK_TREE_VIEW (session_list), &path, &column); cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column)); if (cells != NULL) { GtkCellRenderer *cell; cell = (GtkCellRenderer *) cells->data; g_list_free (cells); g_object_set (cell, "editable", TRUE, NULL); gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (session_list), path, column, cell, TRUE); } gtk_tree_path_free (path); } static void on_rename_session_clicked (GtkButton *button, gpointer data) { begin_rename (); } static void on_continue_clicked (GtkButton *button, gpointer data) { char *name; name = get_selected_session (); g_free (name); gtk_main_quit (); } static void create_session (const char *name) { char *path; GtkTreeIter iter; path = get_session_path (name); - if (mkdir (path, 0755) < 0) { + if (g_mkdir_with_parents (path, 0755) < 0) { g_warning ("Failed to create directory %s", path); } else { char *marker; gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); marker = g_build_filename (path, ".new-session", NULL); creat (marker, 0600); g_free (marker); } g_free (path); } static gboolean rename_session (const char *old_name, const char *new_name) { char *old_path, *new_path; int result; if (g_strcmp0 (old_name, new_name) == 0) { return TRUE; } if (!is_valid_session_name (new_name)) { return FALSE; } old_path = get_session_path (old_name); new_path = get_session_path (new_name); result = g_rename (old_path, new_path); if (result < 0) { g_warning ("Failed to rename session from '%s' to '%s': %m", old_name, new_name); + } else { + char *last_session; + last_session = get_last_session (); + if (g_strcmp0 (old_name, last_session) == 0) { + make_session_current (new_name); + } + g_free (last_session); } g_free (old_path); g_free (new_path); - return result == 0; } static gboolean -make_session_current (const char *name) -{ - char *path1; - gboolean ret = TRUE; - - path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL); - - unlink (path1); - if (symlink (name, path1) < 0) { - g_warning ("Failed to make session '%s' current", name); - ret = FALSE; - } - - g_free (path1); - - return ret; -} - -static gboolean create_and_select_session (const char *name) { gchar *path; if (name[0] == 0 || name[0] == '.' || strchr (name, '/')) { g_warning ("Invalid session name"); return FALSE; } path = g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); if (!g_file_test (path, G_FILE_TEST_IS_DIR)) { - if (mkdir (path, 0755) < 0) { + if (g_mkdir_with_parents (path, 0755) < 0) { g_warning ("Failed to create directory %s", path); g_free (path); return FALSE; } } g_free (path); return make_session_current (name); } static void select_session (const char *name) { GtkTreeIter iter; char *n; - make_session_current (name); - /* now select it in the list */ gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sort_model), &iter); do { gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &iter, 0, &n, -1); if (strcmp (n, name) == 0) { GtkTreePath *path; path = gtk_tree_model_get_path (GTK_TREE_MODEL (sort_model), &iter); gtk_tree_view_set_cursor (GTK_TREE_VIEW (session_list), path, NULL, FALSE); gtk_tree_path_free (path); g_free (n); break; } g_free (n); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (sort_model), &iter)); } static void -on_new_session_clicked (GtkButton *button, - gpointer data) +create_session_and_begin_rename (void) { gchar *name; name = find_new_session_name (); create_session (name); select_session (name); begin_rename (); } static void +on_new_session_clicked (GtkButton *button, + gpointer data) +{ + create_session_and_begin_rename (); +} + +static void on_selection_changed (GtkTreeSelection *selection, gpointer data) { char *name; name = get_selected_session (); if (name == NULL) { return; } - make_session_current (name); - g_free (name); } static void update_remove_button (void) { GtkWidget *button; button = (GtkWidget *)gtk_builder_get_object (builder, "remove-session"); if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) { gtk_widget_set_sensitive (button, TRUE); } else { gtk_widget_set_sensitive (button, FALSE); } } static void on_row_edited (GtkCellRendererText *cell, const char *path_string, const char *new_name, gpointer data) { GtkTreePath *path; GtkTreeIter sort_iter, items_iter; char *old_name; gboolean was_renamed; path = gtk_tree_path_new_from_string (path_string); gtk_tree_model_get_iter (GTK_TREE_MODEL (sort_model), &sort_iter, path); gtk_tree_model_get (GTK_TREE_MODEL (sort_model), &sort_iter, 0, &old_name, -1); was_renamed = rename_session (old_name, new_name); if (was_renamed) { gtk_tree_model_sort_convert_iter_to_child_iter (sort_model, &items_iter, &sort_iter); gtk_list_store_set (store, &items_iter, 0, g_strdup (new_name), -1); g_free (old_name); - make_session_current (new_name); } else { begin_rename (); } gtk_tree_path_free (path); g_object_set (cell, "editable", FALSE, NULL); } static void on_row_deleted (GtkTreeModel *model, GtkTreePath *path, gpointer data) { update_remove_button (); } static void on_row_inserted (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { update_remove_button (); } static void on_row_activated (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { - char *name; - - name = get_selected_session (); - g_free (name); - gtk_main_quit (); } static void auto_save_next_session (void) { GSettings *settings; settings = g_settings_new (GSM_MANAGER_SCHEMA); g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, TRUE); g_object_unref (settings); } static void auto_save_next_session_if_needed (void) { char *marker; marker = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", ".new-session", NULL); if (g_file_test (marker, G_FILE_TEST_EXISTS)) { auto_save_next_session (); unlink (marker); } g_free (marker); } +static void +save_session (void) +{ + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error; + + conn = dbus_g_bus_get (DBUS_BUS_SESSION, NULL); + if (conn == NULL) { + g_warning ("Could not connect to the session bus"); + return; + } + + proxy = dbus_g_proxy_new_for_name (conn, GSM_SERVICE_DBUS, GSM_PATH_DBUS, GSM_INTERFACE_DBUS); + if (proxy == NULL) { + g_warning ("Could not connect to the session manager"); + return; + } + + error = NULL; + if (!dbus_g_proxy_call (proxy, "SaveSession", &error, G_TYPE_INVALID, G_TYPE_INVALID)) { + g_warning ("Failed to save session: %s", error->message); + g_error_free (error); + return; + } + + g_object_unref (proxy); +} + static int compare_sessions (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer data) { char *name_a, *name_b; int result; gtk_tree_model_get (model, a, 0, &name_a, -1); gtk_tree_model_get (model, b, 0, &name_b, -1); result = g_utf8_collate (name_a, name_b); g_free (name_a); g_free (name_b); return result; } static void on_map (GtkWidget *widget, gpointer data) { gdk_window_focus (gtk_widget_get_window (widget), GDK_CURRENT_TIME); } int main (int argc, char *argv[]) { GtkWidget *window; GtkWidget *widget; + GtkWidget *label; GtkCellRenderer *cell; GtkTreeViewColumn *column; GtkTreeSelection *selection; GError *error; + char *selected_session; + + static char *action = NULL; + static char **remaining_args = NULL; + static GOptionEntry entries[] = { + {"action", '\0', 0, G_OPTION_ARG_STRING, &action, N_("What to do with session selection (save|load|print)"), NULL}, +{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args, N_("[session-name]"), NULL} + }; + + if (action == NULL) { + if (getenv ("SESSION_MANAGER") != NULL) + action = "print"; + else + action = "load"; + } - if (getenv ("SESSION_MANAGER") != NULL) + if (getenv ("SESSION_MANAGER") != NULL && strcmp (action, "load") == 0) { + g_warning ("Cannot load new session when session currently loaded"); return 1; + } + + if (getenv ("SESSION_MANAGER") == NULL && strcmp (action, "save") == 0) { + g_warning ("Can only save session when session loaded"); + return 1; + } + + if (strcmp (action, "load") != 0 && strcmp (action, "save") != 0 && strcmp (action, "print") != 0) { + g_warning ("'%s' is not a supported action. Supported actions are load, save, and print.\n", action); + return 1; + } - gtk_init (&argc, &argv); - if (argc > 1) { - g_print ("create and select session\n"); - if (!create_and_select_session (argv[1])) + error = NULL; + gtk_init_with_args (&argc, &argv, + NULL, entries, GETTEXT_PACKAGE, &error); + + if (remaining_args != NULL) { + if (g_strv_length (remaining_args) > 1) { + g_warning ("gnome-session-selector takes at most one session argument"); + return 1; + } + + if (!create_and_select_session (remaining_args[0])) return 1; else return 0; } builder = gtk_builder_new (); gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE); error = NULL; if (!gtk_builder_add_from_file (builder, GTKBUILDER_DIR "/" "session-selector.ui", &error)) { g_warning ("Could not load file 'session-selector.ui': %s", error->message); exit (1); } window = (GtkWidget *) gtk_builder_get_object (builder, "main-window"); store = (GtkListStore *) gtk_builder_get_object (builder, "session-store"); sort_model = (GtkTreeModelSort *) gtk_builder_get_object (builder, "sort-model"); gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (sort_model), 0, compare_sessions, NULL, NULL); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), 0, GTK_SORT_ASCENDING); g_signal_connect (store, "row-deleted", G_CALLBACK (on_row_deleted), NULL); g_signal_connect (store, "row-inserted", G_CALLBACK (on_row_inserted), NULL); session_list = (GtkWidget *) gtk_builder_get_object (builder, "session-list"); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); populate_session_list (session_list); cell = gtk_cell_renderer_text_new (); g_signal_connect (cell, "edited", G_CALLBACK (on_row_edited), NULL); column = gtk_tree_view_column_new_with_attributes ("", cell, "text", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (session_list), GTK_TREE_VIEW_COLUMN (column)); g_signal_connect (session_list, "row-activated", G_CALLBACK (on_row_activated), NULL); g_signal_connect (selection, "changed", G_CALLBACK (on_selection_changed), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "new-session"); g_signal_connect (widget, "clicked", G_CALLBACK (on_new_session_clicked), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "remove-session"); g_signal_connect (widget, "clicked", G_CALLBACK (on_remove_session_clicked), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "rename-session"); g_signal_connect (widget, "clicked", G_CALLBACK (on_rename_session_clicked), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "continue-button"); g_signal_connect (widget, "clicked", G_CALLBACK (on_continue_clicked), NULL); g_signal_connect (window, "map", G_CALLBACK (on_map), NULL); gtk_widget_show (window); + if (g_strcmp0 (action, "load") == 0) { + info_text = _("Please select a custom session to run"); + } else if (g_strcmp0 (action, "print") == 0) { + info_text = _("Please select a session to use"); + } else if (g_strcmp0 (action, "save") == 0) { + info_text = _("Please select a session to save to"); + } + + label = (GtkWidget*) gtk_builder_get_object (builder, "info-label"); + gtk_label_set_markup (GTK_LABEL (label), info_text); + + selected_session = get_selected_session (); + + if (selected_session == NULL) { + create_session_and_begin_rename (); + } else { + g_free (selected_session); + } + gtk_main (); - auto_save_next_session_if_needed (); + selected_session = get_selected_session (); + + if (g_strcmp0 (action, "load") == 0) { + make_session_current (selected_session); + auto_save_next_session_if_needed (); + } else if (g_strcmp0 (action, "save") == 0) { + char *last_session; + + last_session = get_last_session (); + make_session_current (selected_session); + save_session (); + if (last_session != NULL) + make_session_current (last_session); + } else if (g_strcmp0 (action, "print") == 0) { + g_print ("%s\n", selected_session); + } + g_free (selected_session); return 0; } -- 2.3.7 From ebf36d525604940d33f23732ddeb6c8ab306a7b0 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 11:32:52 -0500 Subject: [PATCH 12/17] make save-session stall until it finishes --- gnome-session/gsm-manager.c | 58 ++++++++++++++++++++++++++---- gnome-session/gsm-manager.h | 2 +- gnome-session/org.gnome.SessionManager.xml | 1 + 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c index 67e0c82..2af3358 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -114,60 +114,61 @@ typedef enum GSM_MANAGER_LOGOUT_REBOOT_INTERACT, GSM_MANAGER_LOGOUT_SHUTDOWN, GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT, } GsmManagerLogoutType; struct GsmManagerPrivate { gboolean failsafe; GsmStore *clients; GsmStore *inhibitors; GsmInhibitorFlag inhibited_actions; GsmStore *apps; GsmPresence *presence; GsmXsmpServer *xsmp_server; char *session_name; gboolean is_fallback_session : 1; /* Current status */ GsmManagerPhase phase; guint phase_timeout_id; GSList *required_apps; GSList *pending_apps; GsmManagerLogoutMode logout_mode; GSList *query_clients; guint query_timeout_id; /* This is used for GSM_MANAGER_PHASE_END_SESSION only at the moment, * since it uses a sublist of all running client that replied in a * specific way */ GSList *next_query_clients; + GSList *pending_save_invocations; /* This is the action that will be done just before we exit */ GsmManagerLogoutType logout_type; /* List of clients which were disconnected due to disabled condition * and shouldn't be automatically restarted */ GSList *condition_clients; GSList *pending_end_session_tasks; GCancellable *end_session_cancellable; GSettings *settings; GSettings *session_settings; GSettings *screensaver_settings; GSettings *lockdown_settings; GsmSystem *system; DBusGProxy *bus_proxy; DBusGConnection *connection; gboolean dbus_disconnected : 1; GsmShell *shell; guint shell_end_session_dialog_canceled_id; guint shell_end_session_dialog_open_failed_id; guint shell_end_session_dialog_confirmed_logout_id; guint shell_end_session_dialog_confirmed_shutdown_id; guint shell_end_session_dialog_confirmed_reboot_id; }; enum { PROP_0, @@ -1208,90 +1209,124 @@ query_end_session_complete (GsmManager *manager) static gboolean _client_request_save (GsmClient *client, ClientEndSessionData *data) { gboolean ret; GError *error; error = NULL; ret = gsm_client_request_save (client, data->flags, &error); if (ret) { g_debug ("GsmManager: adding client to query clients: %s", gsm_client_peek_id (client)); data->manager->priv->query_clients = g_slist_prepend (data->manager->priv->query_clients, client); } else if (error) { g_debug ("GsmManager: unable to query client: %s", error->message); g_error_free (error); } return FALSE; } static gboolean _client_request_save_helper (const char *id, GsmClient *client, ClientEndSessionData *data) { return _client_request_save (client, data); } static void +fail_pending_save_invocations (GsmManager *manager, + GError *error) +{ + GSList *l; + + for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) { + DBusGMethodInvocation *context = l->data; + + dbus_g_method_return_error (context, error); + } + + g_slist_free (manager->priv->pending_save_invocations); + manager->priv->pending_save_invocations = NULL; +} + +static void +finish_pending_save_invocations (GsmManager *manager) +{ + GSList *l; + + for (l = manager->priv->pending_save_invocations; l != NULL; l = l->next) { + DBusGMethodInvocation *context = l->data; + + dbus_g_method_return (context); + } + + g_slist_free (manager->priv->pending_save_invocations); + manager->priv->pending_save_invocations = NULL; +} + +static void query_save_session_complete (GsmManager *manager) { GError *error = NULL; if (g_slist_length (manager->priv->next_query_clients) > 0) { ClientEndSessionData data; data.manager = manager; data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; g_slist_foreach (manager->priv->next_query_clients, (GFunc)_client_request_save, &data); g_slist_free (manager->priv->next_query_clients); manager->priv->next_query_clients = NULL; return; } if (manager->priv->query_timeout_id > 0) { g_source_remove (manager->priv->query_timeout_id); manager->priv->query_timeout_id = 0; } gsm_session_save (manager->priv->clients, &error); if (error) { g_warning ("Error saving session: %s", error->message); + fail_pending_save_invocations (manager, error); g_error_free (error); + } else { + finish_pending_save_invocations (manager); } } static guint32 generate_cookie (void) { guint32 cookie; cookie = (guint32)g_random_int_range (1, G_MAXINT32); return cookie; } static guint32 _generate_unique_cookie (GsmManager *manager) { guint32 cookie; do { cookie = generate_cookie (); } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); return cookie; } static gboolean _on_query_end_session_timeout (GsmManager *manager) { GSList *l; @@ -3238,92 +3273,101 @@ gsm_manager_reboot (GsmManager *manager, if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { error = g_error_new (GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_NOT_IN_RUNNING, "Reboot interface is only available during the Running phase"); dbus_g_method_return_error (context, error); g_error_free (error); return FALSE; } if (_log_out_is_locked_down (manager)) { error = g_error_new (GSM_MANAGER_ERROR, GSM_MANAGER_ERROR_LOCKED_DOWN, "Logout has been locked down"); dbus_g_method_return_error (context, error); g_error_free (error); return FALSE; } task = g_task_new (manager, manager->priv->end_session_cancellable, (GAsyncReadyCallback) complete_end_session_task, context); manager->priv->pending_end_session_tasks = g_slist_prepend (manager->priv->pending_end_session_tasks, task); request_reboot (manager); return TRUE; } gboolean -gsm_manager_save_session (GsmManager *manager, - GError **error) +gsm_manager_save_session (GsmManager *manager, + DBusGMethodInvocation *context) { ClientEndSessionData data; + GError *error; g_debug ("GsmManager: SaveSession called"); g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { - g_set_error (error, - GSM_MANAGER_ERROR, - GSM_MANAGER_ERROR_NOT_IN_RUNNING, - "SaveSession interface is only available during the Running phase"); + error = g_error_new (GSM_MANAGER_ERROR, + GSM_MANAGER_ERROR_NOT_IN_RUNNING, + "SaveSession interface is only available during the Running phase"); + dbus_g_method_return_error (context, error); + g_error_free (error); return FALSE; } data.manager = manager; data.flags = 0; gsm_store_foreach (manager->priv->clients, (GsmStoreFunc)_client_request_save_helper, &data); if (manager->priv->query_clients) { manager->priv->query_timeout_id = g_timeout_add_seconds (GSM_MANAGER_SAVE_SESSION_TIMEOUT, (GSourceFunc)_on_query_save_session_timeout, manager); + + manager->priv->pending_save_invocations = g_slist_prepend (manager->priv->pending_save_invocations, + context); + return TRUE; } else { g_debug ("GsmManager: Nothing to save"); - return FALSE; + dbus_g_method_return (context); + return TRUE; } + + return TRUE; } gboolean gsm_manager_can_shutdown (GsmManager *manager, gboolean *shutdown_available, GError **error) { g_debug ("GsmManager: CanShutdown called"); g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); *shutdown_available = !_log_out_is_locked_down (manager) && (gsm_system_can_stop (manager->priv->system) || gsm_system_can_restart (manager->priv->system) || gsm_system_can_suspend (manager->priv->system) || gsm_system_can_hibernate (manager->priv->system)); return TRUE; } gboolean gsm_manager_logout (GsmManager *manager, guint logout_mode, GError **error) { g_debug ("GsmManager: Logout called"); g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING) { diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h index bc16654..c365596 100644 --- a/gnome-session/gsm-manager.h +++ b/gnome-session/gsm-manager.h @@ -138,61 +138,61 @@ void _gsm_manager_set_active_session (GsmManager * /* exported methods */ gboolean gsm_manager_register_client (GsmManager *manager, const char *app_id, const char *client_startup_id, DBusGMethodInvocation *context); gboolean gsm_manager_unregister_client (GsmManager *manager, const char *session_client_id, DBusGMethodInvocation *context); gboolean gsm_manager_inhibit (GsmManager *manager, const char *app_id, guint toplevel_xid, const char *reason, guint flags, DBusGMethodInvocation *context); gboolean gsm_manager_uninhibit (GsmManager *manager, guint inhibit_cookie, DBusGMethodInvocation *context); gboolean gsm_manager_is_inhibited (GsmManager *manager, guint flags, gboolean *is_inhibited, GError *error); gboolean gsm_manager_shutdown (GsmManager *manager, DBusGMethodInvocation *context); gboolean gsm_manager_reboot (GsmManager *manager, DBusGMethodInvocation *context); gboolean gsm_manager_save_session (GsmManager *manager, - GError **error); + DBusGMethodInvocation *context); gboolean gsm_manager_can_shutdown (GsmManager *manager, gboolean *shutdown_available, GError **error); gboolean gsm_manager_logout (GsmManager *manager, guint logout_mode, GError **error); gboolean gsm_manager_setenv (GsmManager *manager, const char *variable, const char *value, GError **error); gboolean gsm_manager_get_locale (GsmManager *manager, int category, const char **value, GError **error); gboolean gsm_manager_initialization_error (GsmManager *manager, const char *message, gboolean fatal, GError **error); gboolean gsm_manager_get_clients (GsmManager *manager, GPtrArray **clients, GError **error); gboolean gsm_manager_get_inhibitors (GsmManager *manager, GPtrArray **inhibitors, GError **error); gboolean gsm_manager_is_autostart_condition_handled (GsmManager *manager, const char *condition, gboolean *handled, diff --git a/gnome-session/org.gnome.SessionManager.xml b/gnome-session/org.gnome.SessionManager.xml index eacac4b..81d3da1 100644 --- a/gnome-session/org.gnome.SessionManager.xml +++ b/gnome-session/org.gnome.SessionManager.xml @@ -262,60 +262,61 @@ True if condition is handled, false otherwise Allows the caller to determine whether the session manager is handling changes to the specified autostart condition. Request a shutdown dialog. Request a reboot dialog. + Request to save session True if shutdown is available to the user, false otherwise Allows the caller to determine whether or not it's okay to show a shutdown option in the UI The type of logout that is being requested Request a logout dialog -- 2.3.7 From b7ed1a7fd45a0dac66353f8ec5b8f30a5f0207a8 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 15:32:04 -0500 Subject: [PATCH 13/17] manager: save session type in session dir If a user saved their session when in classic mode, make sure we record that information so subsequent calls to gnome-session will restore classic mode. --- gnome-session/gsm-manager.c | 21 ++++++++++++++++++-- gnome-session/gsm-manager.h | 1 + gnome-session/gsm-session-save.c | 41 +++++++++++++++++++++++++++++++++++++--- gnome-session/gsm-session-save.h | 5 +++-- gnome-session/main.c | 14 +++++++++++--- 5 files changed, 72 insertions(+), 10 deletions(-) diff --git a/gnome-session/gsm-manager.c b/gnome-session/gsm-manager.c index 2af3358..8a8943d 100644 --- a/gnome-session/gsm-manager.c +++ b/gnome-session/gsm-manager.c @@ -1265,61 +1265,61 @@ finish_pending_save_invocations (GsmManager *manager) g_slist_free (manager->priv->pending_save_invocations); manager->priv->pending_save_invocations = NULL; } static void query_save_session_complete (GsmManager *manager) { GError *error = NULL; if (g_slist_length (manager->priv->next_query_clients) > 0) { ClientEndSessionData data; data.manager = manager; data.flags = GSM_CLIENT_END_SESSION_FLAG_LAST; g_slist_foreach (manager->priv->next_query_clients, (GFunc)_client_request_save, &data); g_slist_free (manager->priv->next_query_clients); manager->priv->next_query_clients = NULL; return; } if (manager->priv->query_timeout_id > 0) { g_source_remove (manager->priv->query_timeout_id); manager->priv->query_timeout_id = 0; } - gsm_session_save (manager->priv->clients, &error); + gsm_session_save (manager->priv->clients, manager->priv->session_name, &error); if (error) { g_warning ("Error saving session: %s", error->message); fail_pending_save_invocations (manager, error); g_error_free (error); } else { finish_pending_save_invocations (manager); } } static guint32 generate_cookie (void) { guint32 cookie; cookie = (guint32)g_random_int_range (1, G_MAXINT32); return cookie; } static guint32 _generate_unique_cookie (GsmManager *manager) { guint32 cookie; do { cookie = generate_cookie (); } while (gsm_store_find (manager->priv->inhibitors, (GsmStoreFunc)_find_by_cookie, &cookie) != NULL); return cookie; @@ -1532,60 +1532,77 @@ debug_app_summary (GsmManager *manager) g_debug ("GsmManager: App startup summary"); for (phase = GSM_MANAGER_PHASE_EARLY_INITIALIZATION; phase < GSM_MANAGER_PHASE_RUNNING; phase++) { g_debug ("GsmManager: Phase %s", phase_num_to_name (phase)); gsm_store_foreach (manager->priv->apps, (GsmStoreFunc)_debug_app_for_phase, GUINT_TO_POINTER (phase)); } } void gsm_manager_start (GsmManager *manager) { g_debug ("GsmManager: GSM starting to manage"); g_return_if_fail (GSM_IS_MANAGER (manager)); gsm_xsmp_server_start (manager->priv->xsmp_server); gsm_manager_set_phase (manager, GSM_MANAGER_PHASE_EARLY_INITIALIZATION); debug_app_summary (manager); start_phase (manager); } const char * _gsm_manager_get_default_session (GsmManager *manager) { return g_settings_get_string (manager->priv->session_settings, KEY_SESSION_NAME); } +char * +_gsm_manager_get_saved_session (GsmManager *manager) +{ + char *file; + char *type; + gboolean loaded; + + file = g_build_filename (gsm_util_get_saved_session_dir (), "type", NULL); + loaded = g_file_get_contents (file, &type, NULL, NULL); + g_free (file); + + if (!loaded) + return NULL; + + return type; +} + void _gsm_manager_set_active_session (GsmManager *manager, const char *session_name, gboolean is_fallback) { g_free (manager->priv->session_name); manager->priv->session_name = g_strdup (session_name); manager->priv->is_fallback_session = is_fallback; } static gboolean _app_has_app_id (const char *id, GsmApp *app, const char *app_id_a) { const char *app_id_b; app_id_b = gsm_app_peek_app_id (app); return (app_id_b != NULL && strcmp (app_id_a, app_id_b) == 0); } static GsmApp * find_app_for_app_id (GsmManager *manager, const char *app_id) { GsmApp *app; app = (GsmApp *)gsm_store_find (manager->priv->apps, (GsmStoreFunc)_app_has_app_id, (char *)app_id); return app; @@ -2070,61 +2087,61 @@ on_xsmp_client_register_confirmed (GsmXSMPClient *client, } } static gboolean auto_save_is_enabled (GsmManager *manager) { return g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE_ONE_SHOT) || g_settings_get_boolean (manager->priv->settings, KEY_AUTOSAVE); } static void maybe_save_session (GsmManager *manager) { GError *error; if (gsm_system_is_login_session (manager->priv->system)) return; /* We only allow session saving when session is running or when * logging out */ if (manager->priv->phase != GSM_MANAGER_PHASE_RUNNING && manager->priv->phase != GSM_MANAGER_PHASE_END_SESSION) { return; } if (!auto_save_is_enabled (manager)) { return; } error = NULL; - gsm_session_save (manager->priv->clients, &error); + gsm_session_save (manager->priv->clients, manager->priv->session_name, &error); if (error) { g_warning ("Error saving session: %s", error->message); g_error_free (error); } } static void _handle_client_end_session_response (GsmManager *manager, GsmClient *client, gboolean is_ok, gboolean do_last, gboolean cancel, const char *reason) { /* just ignore if we are not yet running */ if (manager->priv->phase < GSM_MANAGER_PHASE_RUNNING) { return; } g_debug ("GsmManager: Response from end session request: is-ok=%d do-last=%d cancel=%d reason=%s", is_ok, do_last, cancel, reason ? reason :""); if (manager->priv->phase == GSM_MANAGER_PHASE_RUNNING) { /* Ignore responses when no requests were sent */ if (manager->priv->query_clients == NULL) { return; } manager->priv->query_clients = g_slist_remove (manager->priv->query_clients, client); diff --git a/gnome-session/gsm-manager.h b/gnome-session/gsm-manager.h index c365596..d10879b 100644 --- a/gnome-session/gsm-manager.h +++ b/gnome-session/gsm-manager.h @@ -103,60 +103,61 @@ typedef enum } GsmManagerError; #define GSM_MANAGER_ERROR gsm_manager_error_quark () GType gsm_manager_error_get_type (void); #define GSM_MANAGER_TYPE_ERROR (gsm_manager_error_get_type ()) GQuark gsm_manager_error_quark (void); GType gsm_manager_get_type (void); GsmManager * gsm_manager_new (GsmStore *client_store, gboolean failsafe); GsmManager * gsm_manager_get (void); gboolean gsm_manager_get_failsafe (GsmManager *manager); gboolean gsm_manager_add_autostart_app (GsmManager *manager, const char *path, const char *provides); gboolean gsm_manager_add_required_app (GsmManager *manager, const char *path, const char *provides); gboolean gsm_manager_add_autostart_apps_from_dir (GsmManager *manager, const char *path); gboolean gsm_manager_add_legacy_session_apps (GsmManager *manager, const char *path); void gsm_manager_start (GsmManager *manager); const char * _gsm_manager_get_default_session (GsmManager *manager); +char * _gsm_manager_get_saved_session (GsmManager *manager); void _gsm_manager_set_active_session (GsmManager *manager, const char *session_name, gboolean is_fallback); /* exported methods */ gboolean gsm_manager_register_client (GsmManager *manager, const char *app_id, const char *client_startup_id, DBusGMethodInvocation *context); gboolean gsm_manager_unregister_client (GsmManager *manager, const char *session_client_id, DBusGMethodInvocation *context); gboolean gsm_manager_inhibit (GsmManager *manager, const char *app_id, guint toplevel_xid, const char *reason, guint flags, DBusGMethodInvocation *context); gboolean gsm_manager_uninhibit (GsmManager *manager, guint inhibit_cookie, DBusGMethodInvocation *context); gboolean gsm_manager_is_inhibited (GsmManager *manager, guint flags, gboolean *is_inhibited, GError *error); gboolean gsm_manager_shutdown (GsmManager *manager, diff --git a/gnome-session/gsm-session-save.c b/gnome-session/gsm-session-save.c index ea23be5..06eeeb1 100644 --- a/gnome-session/gsm-session-save.c +++ b/gnome-session/gsm-session-save.c @@ -1,73 +1,105 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * gsm-session-save.c * Copyright (C) 2008 Lucas Rocha. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include +#include + #include #include #include #include "gsm-util.h" #include "gsm-autostart-app.h" #include "gsm-client.h" #include "gsm-session-save.h" #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" static gboolean gsm_session_clear_saved_session (const char *directory, GHashTable *discard_hash); typedef struct { char *dir; GHashTable *discard_hash; GError **error; } SessionSaveData; +static void +clear_session_type (const char *save_dir) +{ + char *file; + + file = g_build_filename (save_dir, "type", NULL); + + g_unlink (file); + + g_free (file); +} + +static void +set_session_type (const char *save_dir, + const char *type) +{ + char *file; + GError *error; + + file = g_build_filename (save_dir, "type", NULL); + + error = NULL; + g_file_set_contents (file, type, strlen (type), &error); + if (error != NULL) + g_warning ("couldn't save session type to %s: %s", + type, error->message); + + g_free (file); +} + static gboolean save_one_client (char *id, GObject *object, SessionSaveData *data) { GsmClient *client; GKeyFile *keyfile; const char *app_id; char *path = NULL; char *filename = NULL; char *contents = NULL; gsize length = 0; char *discard_exec; GError *local_error; client = GSM_CLIENT (object); local_error = NULL; keyfile = gsm_client_save (client, &local_error); if (keyfile == NULL || local_error) { goto out; } contents = g_key_file_to_data (keyfile, &length, &local_error); if (local_error) { goto out; } @@ -107,112 +139,114 @@ save_one_client (char *id, GSM_AUTOSTART_APP_DISCARD_KEY, NULL); if (discard_exec) { g_hash_table_insert (data->discard_hash, discard_exec, discard_exec); } g_debug ("GsmSessionSave: saved client %s to %s", id, filename); out: if (keyfile != NULL) { g_key_file_free (keyfile); } g_free (contents); g_free (filename); g_free (path); /* in case of any error, stop saving session */ if (local_error) { g_propagate_error (data->error, local_error); g_error_free (local_error); return TRUE; } return FALSE; } void -gsm_session_save (GsmStore *client_store, - GError **error) +gsm_session_save (GsmStore *client_store, + const char *type, + GError **error) { GSettings *settings; const char *save_dir; char *tmp_dir; SessionSaveData data; g_debug ("GsmSessionSave: Saving session"); /* Clear one shot key autosave in the event its set (so that it's actually * one shot only) */ settings = g_settings_new (GSM_MANAGER_SCHEMA); g_settings_set_boolean (settings, KEY_AUTOSAVE_ONE_SHOT, FALSE); g_object_unref (settings); save_dir = gsm_util_get_saved_session_dir (); if (save_dir == NULL) { g_warning ("GsmSessionSave: cannot create saved session directory"); return; } tmp_dir = gsm_util_get_empty_tmp_session_dir (); if (tmp_dir == NULL) { g_warning ("GsmSessionSave: cannot create new saved session directory"); return; } /* save the session in a temp directory, and remember the discard * commands */ data.dir = tmp_dir; data.discard_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); data.error = error; gsm_store_foreach (client_store, (GsmStoreFunc) save_one_client, &data); if (!*error) { char *session_dir; if (g_file_test (save_dir, G_FILE_TEST_IS_SYMLINK)) session_dir = g_file_read_link (save_dir, error); else session_dir = g_strdup (save_dir); if (session_dir != NULL) { - char *absolute_session_dir; + set_session_type (tmp_dir, type); + if (g_path_is_absolute (session_dir)) { absolute_session_dir = g_strdup (session_dir); } else { char *parent_dir; parent_dir = g_path_get_dirname (save_dir); absolute_session_dir = g_build_filename (parent_dir, session_dir, NULL); g_free (parent_dir); } g_free (session_dir); /* remove the old saved session */ gsm_session_clear_saved_session (absolute_session_dir, data.discard_hash); if (g_file_test (absolute_session_dir, G_FILE_TEST_IS_DIR)) g_rmdir (absolute_session_dir); g_rename (tmp_dir, absolute_session_dir); g_free (absolute_session_dir); } } else { g_warning ("GsmSessionSave: error saving session: %s", (*error)->message); /* FIXME: we should create a hash table filled with the discard * commands that are in desktop files from save_dir. */ gsm_session_clear_saved_session (tmp_dir, NULL); g_rmdir (tmp_dir); } g_hash_table_destroy (data.discard_hash); g_free (tmp_dir); @@ -292,31 +326,32 @@ gsm_session_clear_saved_session (const char *directory, while ((filename = g_dir_read_name (dir))) { char *path = g_build_filename (directory, filename, NULL); result = gsm_session_clear_one_client (path, discard_hash) && result; g_free (path); } g_dir_close (dir); return result; } void gsm_session_save_clear (void) { const char *save_dir; g_debug ("GsmSessionSave: Clearing saved session"); save_dir = gsm_util_get_saved_session_dir (); if (save_dir == NULL) { g_warning ("GsmSessionSave: cannot create saved session directory"); return; } gsm_session_clear_saved_session (save_dir, NULL); + clear_session_type (save_dir); } diff --git a/gnome-session/gsm-session-save.h b/gnome-session/gsm-session-save.h index e623260..c91b561 100644 --- a/gnome-session/gsm-session-save.h +++ b/gnome-session/gsm-session-save.h @@ -1,33 +1,34 @@ /* gsm-session-save.h * Copyright (C) 2008 Lucas Rocha. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __GSM_SESSION_SAVE_H__ #define __GSM_SESSION_SAVE_H__ #include #include "gsm-store.h" G_BEGIN_DECLS -void gsm_session_save (GsmStore *client_store, - GError **error); +void gsm_session_save (GsmStore *client_store, + const char *type, + GError **error); void gsm_session_save_clear (void); G_END_DECLS #endif /* __GSM_SESSION_SAVE_H__ */ diff --git a/gnome-session/main.c b/gnome-session/main.c index fce5e86..52d2aaa 100644 --- a/gnome-session/main.c +++ b/gnome-session/main.c @@ -270,60 +270,61 @@ require_dbus_session (int argc, } static gboolean check_gl (GError **error) { int status; char *argv[] = { LIBEXECDIR "/gnome-session-check-accelerated", NULL }; if (getenv ("DISPLAY") == NULL) { /* Not connected to X11, someone else will take care of checking GL */ return TRUE; } if (!g_spawn_sync (NULL, (char **) argv, NULL, 0, NULL, NULL, NULL, NULL, &status, error)) { return FALSE; } return g_spawn_check_exit_status (status, error); } int main (int argc, char **argv) { GError *error = NULL; GsmManager *manager; GsmStore *client_store; static char **override_autostart_dirs = NULL; static char *opt_session_name = NULL; const char *session_name; + char *saved_session_name = NULL; gboolean gl_failed = FALSE; GOptionContext *options; static GOptionEntry entries[] = { { "autostart", 'a', 0, G_OPTION_ARG_STRING_ARRAY, &override_autostart_dirs, N_("Override standard autostart directories"), N_("AUTOSTART_DIR") }, { "session", 0, 0, G_OPTION_ARG_STRING, &opt_session_name, N_("Session to use"), N_("SESSION_NAME") }, { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, N_("Enable debugging code"), NULL }, { "failsafe", 'f', 0, G_OPTION_ARG_NONE, &failsafe, N_("Do not load user-specified applications"), NULL }, { "version", 0, 0, G_OPTION_ARG_NONE, &show_version, N_("Version of this application"), NULL }, /* Translators: the 'fail whale' is the black dialog we show when something goes seriously wrong */ { "whale", 0, 0, G_OPTION_ARG_NONE, &please_fail, N_("Show the fail whale dialog for testing"), NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; /* Make sure that we have a session bus */ if (!require_dbus_session (argc, argv, &error)) { gsm_util_init_error (TRUE, "%s", error->message); } /* Check GL, if it doesn't work out then force software fallback */ if (!check_gl (&error)) { gl_failed = TRUE; g_debug ("hardware acceleration check failed: %s", error? error->message : ""); g_clear_error (&error); if (g_getenv ("LIBGL_ALWAYS_SOFTWARE") == NULL) { g_setenv ("LIBGL_ALWAYS_SOFTWARE", "1", TRUE); if (!check_gl (&error)) { g_warning ("software acceleration check failed: %s", error? error->message : ""); @@ -402,53 +403,60 @@ main (int argc, char **argv) gsm_util_setenv ("XDG_MENU_PREFIX", "gnome-"); client_store = gsm_store_new (); /* Talk to logind before acquiring a name, since it does synchronous * calls at initialization time that invoke a main loop and if we * already owned a name, then we would service too early during * that main loop. */ g_object_unref (gsm_get_system ()); if (!acquire_name ()) { gsm_fail_whale_dialog_we_failed (TRUE, TRUE, NULL); gsm_main (); exit (1); } manager = gsm_manager_new (client_store, failsafe); g_signal_connect_object (bus_proxy, "destroy", G_CALLBACK (shutdown_cb), manager, G_CONNECT_SWAPPED); g_unix_signal_add (SIGTERM, term_or_int_signal_cb, manager); g_unix_signal_add (SIGINT, term_or_int_signal_cb, manager); g_unix_signal_add (SIGUSR1, sigusr1_cb, manager); g_unix_signal_add (SIGUSR2, sigusr2_cb, manager); - if (IS_STRING_EMPTY (opt_session_name)) - session_name = _gsm_manager_get_default_session (manager); - else + if (IS_STRING_EMPTY (opt_session_name)) { + saved_session_name = _gsm_manager_get_saved_session (manager); + + if (IS_STRING_EMPTY (saved_session_name)) + session_name = _gsm_manager_get_default_session (manager); + else + session_name = saved_session_name; + } else { session_name = opt_session_name; + } gsm_util_set_autostart_dirs (override_autostart_dirs); if (!gsm_session_fill (manager, session_name)) { gsm_fail_whale_dialog_we_failed (FALSE, TRUE, NULL); } gsm_manager_start (manager); gsm_main (); g_clear_object (&manager); g_clear_object (&client_store); g_clear_object (&bus_proxy); + g_free (saved_session_name); gdm_log_shutdown (); return 0; } -- 2.3.7 From c9c7daa61e77f86a4b3cd53ae55af3ad93e8b406 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 7 Jan 2014 21:16:23 -0500 Subject: [PATCH 14/17] session-selector: restore saved session mode When using the custom session selector, we need to know whether to use classic mode or not. This commit makes us use whatever mode was saved with the session. --- tools/gnome-session-custom-session | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tools/gnome-session-custom-session b/tools/gnome-session-custom-session index 07fdb0c..358aee0 100644 --- a/tools/gnome-session-custom-session +++ b/tools/gnome-session-custom-session @@ -1,4 +1,19 @@ #! /bin/sh gnome-session-selector -exec gnome-session + +type_file="${XDG_CONFIG_HOME:-$HOME/.config}/gnome-session/saved-session/type" + +session_type="" +if [ -e "$type_file" ]; then + read session_type < "$type_file" +fi + +session_type_argument="" +[ -n "$session_type" ] && session_type_argument="--session=$session_type" + +if [ "$session_type" = "gnome-classic" ]; then + export GNOME_SHELL_SESSION_MODE="classic" +fi + +exec gnome-session "$session_type_argument" -- 2.3.7 From ded037a059a781042d61e3bc7ef1ced0c9ee706f Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 20 Dec 2013 10:53:33 -0500 Subject: [PATCH 15/17] session-selector: refresh from recent glade The ui file is rather old. This commit just opens it up in a recent glade and resaves it, so we have a fresh starting point to make changes. --- data/session-selector.ui | 80 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/data/session-selector.ui b/data/session-selector.ui index 1534a74..4d1e300 100644 --- a/data/session-selector.ui +++ b/data/session-selector.ui @@ -1,195 +1,223 @@ - + - - - - - - - - - - session-store - + + False Custom Session center 500 310 False True + False 0.5 out True + False 12 True - vertical + False 6 - True - other - + False + other - + True + False vertical - 0 True - 0.0 - 0.5 + False + 0 Please select a custom session to use - True - True + False + False 0 + + False + False + 0 + + + + + False + + + False + True + 1 + False True 0 True - vertical + False 12 True + False 12 True True never - automatic in True True + sort-model False 0 - sort-model + + + + True + True 0 True - vertical + False 6 start _New Session True True True True False False 0 _Remove Session True True True True False False 1 Rena_me Session True True True True False False 2 False + True 1 + True + True 1 + True + True 1 True + False 6 end _Continue True True True True True True False False 0 False + True 2 + + + + + + + + session-store + -- 2.3.7 From 34d666e847c142545c3de244df08705f6a19b933 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Tue, 7 Jan 2014 21:02:02 -0500 Subject: [PATCH 16/17] session-selector: add toggle for classic/normal selection Since we offer both classic mode and regular mode when not using the session selector, we should also offer it when using the session selector. --- data/session-selector.ui | 39 ++++++++++++++- tools/gnome-session-selector.c | 105 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 2 deletions(-) diff --git a/data/session-selector.ui b/data/session-selector.ui index 4d1e300..beab73a 100644 --- a/data/session-selector.ui +++ b/data/session-selector.ui @@ -153,71 +153,106 @@ False 2 False True 1 True True 1 True True 1 True False 6 - end + + + True + False + 6 + + + True + False + Classic Experience + + + False + True + 0 + + + + + True + True + + + False + True + 1 + + + + + False + True + 0 + + _Continue True True True True True True False False - 0 + 1 + True False True 2 session-store diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c index 8f37eca..dbe3bda 100644 --- a/tools/gnome-session-selector.c +++ b/tools/gnome-session-selector.c @@ -16,60 +16,61 @@ * along with this program; if not, see . * * Written by: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define GSM_SERVICE_DBUS "org.gnome.SessionManager" #define GSM_PATH_DBUS "/org/gnome/SessionManager" #define GSM_INTERFACE_DBUS "org.gnome.SessionManager" #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" +#define DEFAULT_SESSION_NAME "gnome" static GtkBuilder *builder; static GtkWidget *session_list; static GtkListStore *store; static GtkTreeModelSort *sort_model; static char *info_text; static void select_session (const char *name); static gboolean make_session_current (const char *name); static char * get_session_path (const char *name) { return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); } static char * find_new_session_name (void) { char *name; char *path; int i; for (i = 1; i < 20; i++) { name = g_strdup_printf (_("Session %d"), i); path = get_session_path (name); if (!g_file_test (path, G_FILE_TEST_EXISTS)) { g_free (path); return name; } @@ -125,104 +126,126 @@ is_valid_session_name (const char *name) gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter); do { gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &n, -1); if (strcmp (n, name) == 0) { char *message; message = g_strdup_printf (_("A session named ā€˜%sā€™ already exists"), name); warning_text = g_strdup_printf ("%s\nNote: %s", info_text, message); g_free (message); g_free (n); break; } g_free (n); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter)); info_bar = (GtkWidget *) gtk_builder_get_object (builder, "info-bar"); label = (GtkWidget*) gtk_builder_get_object (builder, "info-label"); if (warning_text != NULL) { gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_WARNING); gtk_label_set_markup (GTK_LABEL (label), warning_text); g_free (warning_text); return FALSE; } gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_OTHER); gtk_label_set_markup (GTK_LABEL (label), info_text); return TRUE; } +static char * +get_session_type_from_file (const char *name) +{ + char *file; + char *type; + gboolean loaded; + + file = g_build_filename (g_get_user_config_dir (), "gnome-session", name, "type", NULL); + loaded = g_file_get_contents (file, &type, NULL, NULL); + g_free (file); + + if (!loaded) + return g_strdup (DEFAULT_SESSION_NAME); + + return type; +} + static void populate_session_list (GtkWidget *session_list) { GtkTreeIter iter; char *path; const char *name; GDir *dir; GError *error; char *saved_session; char *default_session; char *default_name; char last_session[PATH_MAX] = ""; saved_session = get_session_path ("saved-session"); if (!g_file_test (saved_session, G_FILE_TEST_IS_SYMLINK)) { default_name = find_new_session_name (); default_session = get_session_path (default_name); rename (saved_session, default_session); if (symlink (default_name, saved_session) < 0) g_warning ("Failed to convert saved-session to symlink"); g_free (default_name); g_free (default_session); } path = g_build_filename (g_get_user_config_dir (), "gnome-session", NULL); error = NULL; dir = g_dir_open (path, 0, &error); if (dir == NULL) { g_warning ("Failed to open %s: %s", path, error->message); g_error_free (error); goto out; } default_name = NULL; if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { default_name = g_path_get_basename (last_session); } while ((name = g_dir_read_name (dir)) != NULL) { + char *session_type; + if (strcmp (name, "saved-session") == 0) continue; + session_type = get_session_type_from_file (name); + gtk_list_store_insert_with_values (store, &iter, 100, 0, name, -1); + g_free (session_type); if (g_strcmp0 (default_name, name) == 0) { GtkTreeSelection *selection; GtkTreeIter child_iter; gtk_tree_model_sort_convert_child_iter_to_iter (GTK_TREE_MODEL_SORT (sort_model), &child_iter, &iter); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); gtk_tree_selection_select_iter (selection, &child_iter); } } g_free (default_name); g_dir_close (dir); out: g_free (saved_session); g_free (path); } static char * get_last_session (void) { char *saved_session; char last_session[PATH_MAX] = ""; char *name = NULL; saved_session = get_session_path ("saved-session"); if (readlink (saved_session, last_session, PATH_MAX - 1) > 0) { name = g_path_get_basename (last_session); @@ -260,60 +283,136 @@ remove_session (const char *name) GError *error; path1 = get_session_path ("saved-session"); path2 = get_session_path (name); error = NULL; n = g_file_read_link (path1, &error); if (n == NULL) { g_warning ("Failed to read link: %s", error->message); g_error_free (error); } else if (strcmp (n, name) == 0) { unlink (path1); } g_free (n); dir = g_dir_open (path2, 0, NULL); while ((d = g_dir_read_name (dir)) != NULL) { path = g_build_filename (path2, d, NULL); unlink (path); g_free (path); } g_dir_close (dir); remove (path2); g_free (path1); g_free (path2); } +static const char * +get_session_type_from_switch (void) +{ + GtkWidget *mode_switch; + gboolean is_classic_mode; + + mode_switch = (GtkWidget *)gtk_builder_get_object (builder, "classic-mode-switch"); + + is_classic_mode = gtk_switch_get_active (GTK_SWITCH (mode_switch)); + + if (is_classic_mode) { + return "gnome-classic"; + } else { + return "gnome"; + } +} + +static void +set_mode_switch_from_session_type_file (const char *name) +{ + GtkWidget *mode_switch; + gboolean is_classic_mode = FALSE; + char *type; + + mode_switch = (GtkWidget *)gtk_builder_get_object (builder, "classic-mode-switch"); + + type = get_session_type_from_file (name); + is_classic_mode = strcmp (type, "gnome-classic") == 0; + g_free (type); + + gtk_switch_set_active (GTK_SWITCH (mode_switch), is_classic_mode); +} + +static void +save_session_type (const char *save_dir, + const char *type) +{ + char *file; + GError *error; + + file = g_build_filename (save_dir, "type", NULL); + + error = NULL; + g_file_set_contents (file, type, strlen (type), &error); + if (error != NULL) + g_warning ("couldn't save session type to %s: %s", + type, error->message); + + g_free (file); +} + +static void +save_session_type_from_switch (void) +{ + char *name, *path; + const char *session_type; + + name = get_selected_session (); + + if (name == NULL) { + return; + } + + path = get_session_path (name); + g_free (name); + + session_type = get_session_type_from_switch (); + save_session_type (path, session_type); +} + +static void +on_mode_switched (GtkSwitch *mode_switch) +{ + save_session_type_from_switch (); +} + static gboolean make_session_current (const char *name) { char *path1; gboolean ret = TRUE; path1 = g_build_filename (g_get_user_config_dir (), "gnome-session", "saved-session", NULL); unlink (path1); if (symlink (name, path1) < 0) { g_warning ("Failed to make session '%s' current", name); ret = FALSE; } g_free (path1); return ret; } static void on_remove_session_clicked (GtkButton *button, gpointer data) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeIter iter; char *name; selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { @@ -492,60 +591,62 @@ static void create_session_and_begin_rename (void) { gchar *name; name = find_new_session_name (); create_session (name); select_session (name); begin_rename (); } static void on_new_session_clicked (GtkButton *button, gpointer data) { create_session_and_begin_rename (); } static void on_selection_changed (GtkTreeSelection *selection, gpointer data) { char *name; name = get_selected_session (); if (name == NULL) { return; } + set_mode_switch_from_session_type_file (name); + g_free (name); } static void update_remove_button (void) { GtkWidget *button; button = (GtkWidget *)gtk_builder_get_object (builder, "remove-session"); if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) { gtk_widget_set_sensitive (button, TRUE); } else { gtk_widget_set_sensitive (button, FALSE); } } static void on_row_edited (GtkCellRendererText *cell, const char *path_string, const char *new_name, gpointer data) { GtkTreePath *path; GtkTreeIter sort_iter, items_iter; char *old_name; gboolean was_renamed; path = gtk_tree_path_new_from_string (path_string); gtk_tree_model_get_iter (GTK_TREE_MODEL (sort_model), &sort_iter, path); @@ -751,75 +852,80 @@ main (int argc, char *argv[]) gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sort_model), 0, GTK_SORT_ASCENDING); g_signal_connect (store, "row-deleted", G_CALLBACK (on_row_deleted), NULL); g_signal_connect (store, "row-inserted", G_CALLBACK (on_row_inserted), NULL); session_list = (GtkWidget *) gtk_builder_get_object (builder, "session-list"); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (session_list)); gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE); populate_session_list (session_list); cell = gtk_cell_renderer_text_new (); g_signal_connect (cell, "edited", G_CALLBACK (on_row_edited), NULL); column = gtk_tree_view_column_new_with_attributes ("", cell, "text", 0, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (session_list), GTK_TREE_VIEW_COLUMN (column)); g_signal_connect (session_list, "row-activated", G_CALLBACK (on_row_activated), NULL); g_signal_connect (selection, "changed", G_CALLBACK (on_selection_changed), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "new-session"); g_signal_connect (widget, "clicked", G_CALLBACK (on_new_session_clicked), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "remove-session"); g_signal_connect (widget, "clicked", G_CALLBACK (on_remove_session_clicked), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "rename-session"); g_signal_connect (widget, "clicked", G_CALLBACK (on_rename_session_clicked), NULL); widget = (GtkWidget *) gtk_builder_get_object (builder, "continue-button"); g_signal_connect (widget, "clicked", G_CALLBACK (on_continue_clicked), NULL); + widget = (GtkWidget *) gtk_builder_get_object (builder, "classic-mode-switch"); + g_signal_connect (widget, "notify::active", G_CALLBACK (on_mode_switched), NULL); g_signal_connect (window, "map", G_CALLBACK (on_map), NULL); gtk_widget_show (window); if (g_strcmp0 (action, "load") == 0) { info_text = _("Please select a custom session to run"); } else if (g_strcmp0 (action, "print") == 0) { info_text = _("Please select a session to use"); } else if (g_strcmp0 (action, "save") == 0) { info_text = _("Please select a session to save to"); } label = (GtkWidget*) gtk_builder_get_object (builder, "info-label"); gtk_label_set_markup (GTK_LABEL (label), info_text); selected_session = get_selected_session (); if (selected_session == NULL) { create_session_and_begin_rename (); } else { + set_mode_switch_from_session_type_file (selected_session); g_free (selected_session); } gtk_main (); selected_session = get_selected_session (); if (g_strcmp0 (action, "load") == 0) { make_session_current (selected_session); + save_session_type_from_switch (); auto_save_next_session_if_needed (); } else if (g_strcmp0 (action, "save") == 0) { char *last_session; last_session = get_last_session (); make_session_current (selected_session); save_session (); + save_session_type_from_switch (); if (last_session != NULL) make_session_current (last_session); } else if (g_strcmp0 (action, "print") == 0) { g_print ("%s\n", selected_session); } g_free (selected_session); return 0; } -- 2.3.7 From c056d368938c75b1d6227e84f3daba40e8d39d71 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Wed, 8 Jan 2014 10:15:29 -0500 Subject: [PATCH 17/17] session-selector: use classic mode by default --- tools/gnome-session-selector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/gnome-session-selector.c b/tools/gnome-session-selector.c index dbe3bda..3e0b32e 100644 --- a/tools/gnome-session-selector.c +++ b/tools/gnome-session-selector.c @@ -16,61 +16,61 @@ * along with this program; if not, see . * * Written by: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define GSM_SERVICE_DBUS "org.gnome.SessionManager" #define GSM_PATH_DBUS "/org/gnome/SessionManager" #define GSM_INTERFACE_DBUS "org.gnome.SessionManager" #define GSM_MANAGER_SCHEMA "org.gnome.SessionManager" #define KEY_AUTOSAVE_ONE_SHOT "auto-save-session-one-shot" -#define DEFAULT_SESSION_NAME "gnome" +#define DEFAULT_SESSION_NAME "gnome-classic" static GtkBuilder *builder; static GtkWidget *session_list; static GtkListStore *store; static GtkTreeModelSort *sort_model; static char *info_text; static void select_session (const char *name); static gboolean make_session_current (const char *name); static char * get_session_path (const char *name) { return g_build_filename (g_get_user_config_dir (), "gnome-session", name, NULL); } static char * find_new_session_name (void) { char *name; char *path; int i; for (i = 1; i < 20; i++) { name = g_strdup_printf (_("Session %d"), i); path = get_session_path (name); if (!g_file_test (path, G_FILE_TEST_EXISTS)) { g_free (path); return name; } -- 2.3.7