SF.net SVN: geany:[4484] branches/sm
ntrel at users.sourceforge.net
ntrel at xxxxx
Tue Dec 8 16:36:42 UTC 2009
Revision: 4484
http://geany.svn.sourceforge.net/geany/?rev=4484&view=rev
Author: ntrel
Date: 2009-12-08 16:36:42 +0000 (Tue, 08 Dec 2009)
Log Message:
-----------
Restart Geany and restore some state when logging in (patch by
Eugene Arshinov, thanks).
Modified Paths:
--------------
branches/sm/ChangeLog
branches/sm/src/main.c
Modified: branches/sm/ChangeLog
===================================================================
--- branches/sm/ChangeLog 2009-12-08 16:26:11 UTC (rev 4483)
+++ branches/sm/ChangeLog 2009-12-08 16:36:42 UTC (rev 4484)
@@ -7,6 +7,9 @@
src/document.h, src/main.c, src/main.h:
Refactor quitting code into main_save() and main_finalize() (patch by
Eugene Arshinov, thanks).
+ * src/main.c:
+ Restart Geany and restore some state when logging in (patch by
+ Eugene Arshinov, thanks).
2009-12-08 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
Modified: branches/sm/src/main.c
===================================================================
--- branches/sm/src/main.c 2009-12-08 16:26:11 UTC (rev 4483)
+++ branches/sm/src/main.c 2009-12-08 16:36:42 UTC (rev 4484)
@@ -83,6 +83,11 @@
# include "vte.h"
#endif
+#ifdef HAVE_LIBSM
+# include <X11/SM/SMlib.h>
+# include <X11/ICE/ICElib.h>
+#endif
+
#ifndef N_
# define N_(String) (String)
#endif
@@ -115,6 +120,7 @@
static gboolean no_plugins = FALSE;
#endif
static gboolean dummy = FALSE;
+static gchar * libsm_client_id = NULL;
/* in alphabetical order of short options */
static GOptionEntry entries[] =
@@ -143,10 +149,311 @@
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_mode, N_("Be verbose"), NULL },
{ "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Show version and exit"), NULL },
{ "dummy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &dummy, NULL, NULL }, /* for +NNN line number arguments */
+ { "libsm-client-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &libsm_client_id, NULL, NULL },
{ NULL, 0, 0, 0, NULL, NULL, NULL }
};
+#ifdef HAVE_LIBSM
+ /*
+ * As libSM is not available on Windows,
+ * it is safe enough to use POSIX-specific things here.
+ *
+ * Note that we have to support ICE (Inter-Client Exchange Protocol)
+ * in order to support XSMP (X Session Management Protocol). So both
+ * are initialized here.
+ *
+ * Usage: sm_init() is called from the main() function.
+ */
+
+ /* --- ICE --- */
+
+static gboolean ice_process_messages(GIOChannel * source, GIOCondition condition, gpointer data)
+{
+ IceConn icecon = (IceConn)data;
+ IceProcessMessages(icecon, NULL, NULL);
+ return TRUE;
+}
+
+static void ice_handle_connection(IceConn icecon, IcePointer client_data,
+ Bool opening, IcePointer *watch_data)
+{
+ guint input_id;
+
+ if (opening)
+ {
+ /*
+ * Install a GLib IO Channel to process ICE messages coming from this connection
+ * in our GTK event loop. This is how session manager communicates to us.
+ */
+ GIOChannel * channel = g_io_channel_unix_new(IceConnectionNumber(icecon));
+ input_id = g_io_add_watch(channel, G_IO_IN | G_IO_HUP | G_IO_ERR,
+ ice_process_messages, icecon);
+ g_io_channel_unref(channel);
+
+ *watch_data = (IcePointer)GUINT_TO_POINTER(input_id);
+ }
+ else
+ {
+ input_id = GPOINTER_TO_UINT((gpointer)*watch_data);
+ g_source_remove(input_id);
+ }
+}
+
+ /* --- libSM: data --- */
+
+SmPropValue sm_program_val;
+SmPropValue sm_client_id_arg_val;
+
+ /* --- libSM: utility functions --- */
+
+static void sm_set_command_props(SmcConn smcon)
+{
+ /*
+ * According to libSM documentation, client-ID should be saved as part of the
+ * SmRestartCommand so that the client will retain the same ID after it is
+ * restarted. We use '--libsm-client-id' command-line option for that.
+ */
+
+ /*
+ * FIXME: We have to specify '--no-session' command-line argument in commands.
+ *
+ * Reason:
+ *
+ * Currently all Geany instances try to save session. Consider the
+ * following use case.
+ *
+ * User creates two instances of geany, a "main" one (for example, using
+ * the main menu of his DE) and a "non-main" one typing 'geany --new-instance'
+ * in a terminal emulator. When this user logouts, session manager sends
+ * termination messages to Geany instances in unpredictable order. Geany
+ * session will be saved by the instance that was last to handle the
+ * message. Suppose that was the "non-main" Geany instance.
+ *
+ * When the user logins again, session manager restores Geany instances,
+ * again in unpredictable order. If we do not supply '--no-session' argument,
+ * the "main" instance will catch the session stored by the "non-main"
+ * one, which is not desired behaviour.
+ *
+ * Drawbacks of the '--no-session' solution:
+ *
+ * The "main" instance won't save Geany session as required.
+ *
+ * Possible fixes:
+ *
+ * * Disable saving of Geany session for "non-main" Geany instances.
+ * Sounds sensible and applicable.
+ *
+ * * Save session even when '--no-session' option is specified (i.e.,
+ * consider this options only when reading Geany session). Sounds
+ * non-applicable as the described behaviour does not match
+ * even the option's name.
+ *
+ * * Create a separate option to be used when reading Geany session
+ * is needed and writing Geany session is forbidden. Sounds awkward.
+ */
+
+ const gint page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
+ gint page;
+
+ /*
+ * Allocate space for `page_count+4' elements:
+ * * tree elements for program name, client-ID and '--no-session' option;
+ * * possibly one element for '--new-instance' option;
+ * * max `page_count' elements for file paths.
+ * Store the number of actually used elements in `arr_real_len'.
+ */
+ GArray * arr = g_array_sized_new(FALSE, FALSE, sizeof(SmPropValue), page_count+4);
+ gint arr_real_len = 0;
+ SmPropValue * val;
+ SmProp * prop;
+
+ ((SmPropValue *)arr->data)[0] = sm_program_val;
+ ((SmPropValue *)arr->data)[1] = sm_client_id_arg_val;
+ ((SmPropValue *)arr->data)[2].length = 2; /* length of "-s" */
+ ((SmPropValue *)arr->data)[2].value = "-s";
+ arr_real_len = 3;
+
+#ifdef HAVE_SOCKET
+ if (cl_options.new_instance)
+ {
+ val = ((SmPropValue *)arr->data) + arr_real_len++;
+ val->length = 2; /* length of "-i" */
+ val->value = "-i";
+ }
+#endif
+ /* TODO: handle other command-line options */
+
+ for (page = 0; page < page_count; page++)
+ {
+ GeanyDocument * doc = document_get_from_page(page);
+ if (doc->real_path)
+ {
+ val = ((SmPropValue *)arr->data) + arr_real_len++;
+ val->length = strlen(doc->real_path);
+ val->value = doc->real_path;
+ }
+ }
+
+ SmProp restart_command_prop = {
+ SmRestartCommand,
+ SmLISTofARRAY8,
+ arr_real_len,
+ (SmPropValue *)arr->data};
+ prop = &restart_command_prop;
+ SmcSetProperties(smcon, 1, &prop);
+
+ /*
+ * We should not specify client-ID in SmCloneCommand,
+ * so "remove" the corresponding element from `arr'.
+ */
+ ((SmPropValue *)arr->data)[1] = sm_program_val;
+ SmProp clone_command_prop = {
+ SmCloneCommand,
+ SmLISTofARRAY8,
+ arr_real_len-1,
+ ((SmPropValue *)arr->data)+1};
+ prop = &clone_command_prop;
+ SmcSetProperties(smcon, 1, &prop);
+
+ g_array_free(arr, TRUE);
+}
+
+ /* --- libSM: callbacks --- */
+
+static void sm_interact_callback(SmcConn smcon, SmPointer client_data)
+{
+ gboolean interactive = (gboolean)client_data;
+ gboolean cancelled = !main_save(interactive);
+
+ sm_set_command_props(smcon);
+ SmcInteractDone(smcon, cancelled);
+}
+
+static void sm_die_callback(SmcConn smcon, SmPointer client_data)
+{
+ SmcCloseConnection(smcon, 0, NULL);
+ main_finalize();
+}
+
+static void sm_save_complete_callback(SmcConn smcon, SmPointer client_data)
+{
+}
+
+static void sm_shutdown_cancelled_callback(SmcConn smcon, SmPointer client_data)
+{
+ SmcSaveYourselfDone(smcon, TRUE);
+}
+
+static void sm_save_yourself_callback (SmcConn smcon, SmPointer client_data,
+ int save_style, gboolean shutdown, int interact_style, gboolean fast)
+{
+
+ if ((save_style == SmSaveGlobal || save_style == SmSaveBoth) && document_any_unsaved())
+ {
+ if (!SmcInteractRequest(smcon, SmDialogNormal, sm_interact_callback,
+ (gpointer)(interact_style == SmInteractStyleAny)))
+ SmcSaveYourselfDone(smcon, FALSE);
+
+ return;
+ }
+
+ /*
+ * TODO: libSM documentation says that when the system is being shutting down
+ * (`shutdown' argument is True), we should disable user interaction here
+ * until we get "Die" or "Shutdown" message.
+ */
+
+ sm_set_command_props(smcon);
+ SmcSaveYourselfDone(smcon, TRUE);
+}
+
+ /* --- libSM: sm_init() --- */
+
+static void sm_init(char * argv0)
+{
+ if (!g_getenv("SESSION_MANAGER"))
+ return;
+
+ IceAddConnectionWatch(ice_handle_connection, NULL);
+
+ SmcCallbacks callbacks = {
+ {sm_save_yourself_callback, NULL},
+ {sm_die_callback, NULL},
+ {sm_save_complete_callback, NULL},
+ {sm_shutdown_cancelled_callback, NULL}};
+
+ gchar * client_id;
+ gchar err[256] = "";
+ SmcConn smcon = (gpointer)SmcOpenConnection(NULL, NULL,
+ SmProtoMajor, SmProtoMinor,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ &callbacks,
+ libsm_client_id, &client_id,
+ 256, err);
+
+ if (!smcon)
+ {
+ g_warning("While connecting to session manager:\n%s.", err);
+ return;
+ }
+
+
+ const gchar * username = g_get_user_name();
+ gchar * curdir = g_get_current_dir();
+
+ SmPropValue userid_val = {
+ (username ? strlen(username) : 0),
+ username ? (char *)username : ""};
+ SmPropValue curdir_val = {
+ strlen(curdir),
+ curdir};
+
+ SmProp program_prop = {SmProgram, SmARRAY8, 1, &sm_program_val};
+ SmProp userid_prop = {SmUserID, SmARRAY8, 1, &userid_val};
+ SmProp curdir_prop = {SmCurrentDirectory, SmARRAY8, 1, &curdir_val};
+
+ SmProp * proplist[3] = {
+ &program_prop,
+ &userid_prop,
+ &curdir_prop
+ };
+ SmcSetProperties(smcon, 3, proplist);
+
+
+ /*
+ * Required SmCloneCommand and SmRestartCommand properties are set later
+ * as their values may change in runtime. See also sm_set_command_props().
+ */
+
+ gchar * client_id_arg = g_strconcat("--libsm-client-id=", client_id, NULL); /* never freed */
+
+ /*
+ * If this instance is run with path (e.g., "./geany", "src/geany",
+ * "/usr/local/bin/geany"), specify absolute path in restart command.
+ * Relative paths may not work even if we set SmCurrentDirectory prop.
+ *
+ * Otherwise leave argv[0] as is (geany will be found
+ * using PATH environment variable).
+ */
+ gchar * executable_path = (strchr(argv0, G_DIR_SEPARATOR) == NULL) ? argv0 :
+ g_build_filename(curdir, argv0, NULL); /* never freed */
+
+ sm_program_val.length = strlen(executable_path);
+ sm_program_val.value = executable_path;
+ sm_client_id_arg_val.length = strlen(client_id_arg);
+ sm_client_id_arg_val.value = client_id_arg;
+
+
+ free(client_id);
+ g_free(curdir);
+}
+#endif
+
+
static void setup_window_position(void)
{
/* interprets the saved window geometry */
@@ -948,6 +1255,10 @@
}
#endif
+#ifdef HAVE_LIBSM
+ sm_init(argv[0]);
+#endif
+
geany_debug("Geany %s, GTK+ %u.%u.%u, GLib %u.%u.%u",
main_get_version_string(),
gtk_major_version, gtk_minor_version, gtk_micro_version,
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the Commits
mailing list