Sorry, here are the patches themselves :)
>From 2441b33f2907b8061e0b24aa409466daad934091 Mon Sep 17 00:00:00 2001
From: Eugene Arshinov <[email protected]>
Date: Sun, 13 Dec 2009 14:52:39 +0300
Subject: [PATCH 1/7] Extract SM-related code into separate sm.{c,h} files, make some refactoring, and write code comments for Doxygen.
---
doc/Doxyfile.in | 2 +-
src/Makefile.am | 2 +-
src/main.c | 310 +---------------------------
src/makefile.win32 | 2 +-
src/sm.c | 603 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/sm.h | 28 +++
wscript | 2 +-
7 files changed, 637 insertions(+), 312 deletions(-)
create mode 100644 src/sm.c
create mode 100644 src/sm.h
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 82e96ec..b753373 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -229,7 +229,7 @@ SEARCH_INCLUDES = NO
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
# make G_GNUC_PRINTF a no-op unless doxygen would ignore functions with varargs
-PREDEFINED = "G_GNUC_PRINTF(x,y)=" HAVE_PLUGINS GEANY_FUNCTIONS_H
+PREDEFINED = "G_GNUC_PRINTF(x,y)=" HAVE_PLUGINS GEANY_FUNCTIONS_H HAVE_LIBSM
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = NO
#---------------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index 9f9b276..1a9615d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -38,6 +38,7 @@ SRCS = \
project.c project.h \
sciwrappers.c sciwrappers.h \
search.c search.h \
+ sm.c sm.h \
socket.c socket.h \
stash.c stash.h \
support.h \
@@ -122,4 +123,3 @@ AM_CFLAGS = -DGEANY_DATADIR=\""$(datadir)"\" \
clean-local:
endif
-
diff --git a/src/main.c b/src/main.c
index f5aed42..db7521d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -74,6 +74,7 @@
#include "printing.h"
#include "toolbar.h"
#include "geanyobject.h"
+#include "sm.h"
#ifdef HAVE_SOCKET
# include "socket.h"
@@ -83,11 +84,6 @@
# 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
@@ -154,306 +150,6 @@ static GOptionEntry entries[] =
};
-#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 */
@@ -1255,9 +951,7 @@ gint main(gint argc, gchar **argv)
}
#endif
-#ifdef HAVE_LIBSM
- sm_init(argv[0]);
-#endif
+ sm_init(argv[0], libsm_client_id);
geany_debug("Geany %s, GTK+ %u.%u.%u, GLib %u.%u.%u",
main_get_version_string(),
diff --git a/src/makefile.win32 b/src/makefile.win32
index 344647d..f3e25a2 100644
--- a/src/makefile.win32
+++ b/src/makefile.win32
@@ -65,7 +65,7 @@ OBJS = about.o build.o callbacks.o dialogs.o document.o editor.o encodings.o fil
interface.o keybindings.o keyfile.o \
log.o main.o msgwindow.o navqueue.o notebook.o plugins.o pluginutils.o \
prefs.o printing.o project.o \
- sciwrappers.o search.o socket.o stash.o \
+ sciwrappers.o search.o sm.o socket.o stash.o \
symbols.o templates.o toolbar.o tools.o sidebar.o \
ui_utils.o utils.o win32.o
diff --git a/src/sm.c b/src/sm.c
new file mode 100644
index 0000000..0112274
--- /dev/null
+++ b/src/sm.c
@@ -0,0 +1,603 @@
+/*
+ * sm.c - this file is part of Geany, a fast and lightweight IDE
+ *
+ * Copyright 2009 Eugene Arshinov <earshinov(at)gmail(dot)com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $Id$
+ */
+
+/**
+ * @file sm.c
+ * Provides X session management protocol (XSMP) support using libSM library.
+ *
+ * In order to support XSMP, we have to support Inter-Client Exchange
+ * Protocol (ICE). This file takes care of the latter too.
+ *
+ * Typical usage: @c sm_init() is called when Geany is starting.
+ *
+ * According to libSM documentation, client should retain the same ID after
+ * it is restarted. The main module (@c main.c) maintains "--libsm-client-id"
+ * command-line option and passes the specified value (if any) to @c sm_init().
+ */
+
+/*
+ * As libSM is not available on Windows,
+ * it is safe enough to use POSIX-specific things here.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_LIBSM
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <X11/SM/SMlib.h>
+#include <X11/ICE/ICElib.h>
+
+#include "geany.h"
+#include "main.h" /* for cl_options */
+#include "ui_utils.h" /* access main_widgets.notebook to iterate over opened docs */
+#include "document.h"
+
+
+static void ice_connection_watch(IceConn icecon, IcePointer client_data, Bool opening,
+ IcePointer *watch_data);
+static gboolean ice_iochannel_watch(GIOChannel * source, GIOCondition condition, gpointer data);
+
+static SmcConn sm_connect(const char * libsm_client_id, char ** new_client_id);
+
+static void sm_set_constant_props(SmcConn smcon);
+static void sm_store_props(const char * argv0, const char * libsm_client_id);
+static void sm_set_runtime_props(SmcConn smcon);
+
+static void sm_save_yourself_callback(SmcConn smcon, SmPointer client_data,
+ int save_type, Bool shutdown, int interact_style, Bool fast);
+static void sm_interact_callback(SmcConn smcon, SmPointer client_data);
+static void sm_save_complete_callback(SmcConn smcon, SmPointer client_data);
+static void sm_shutdown_cancelled_callback(SmcConn smcon, SmPointer client_data);
+static void sm_die_callback(SmcConn smcon, SmPointer client_data);
+
+
+/**
+ * @c SmPropValue storing a path to Geany's executable.
+ *
+ * The path can be either an absolute path or just a filename ("geany") to be
+ * resolved using @c PATH.
+ *
+ * The value is used to construct Geany's restart command in @c sm_set_runtime_props().
+ * The variable is initialized in @c sm_store_props().
+ */
+static SmPropValue sm_program_val;
+
+/**
+ * @c SmPropValue storing a string of libSM client ID command-line option and its value.
+ *
+ * The value is used to construct Geany's restart command in @c sm_set_runtime_props().
+ * The variable is initialized in @c sm_store_props().
+ */
+static SmPropValue sm_client_id_arg_val;
+
+#endif
+
+/**
+ * @name Exported functions
+ * @{
+ */
+
+/**
+ * Initialize XSMP support.
+ *
+ * @param argv0 Value of @c argv[0] used to define Geany's restart command.
+ * @param libsm_client_id Client-ID specified with "--libsm-client-id" command line
+ * option or @c NULL if the option was not passed.
+ *
+ * This function connects to the session manager using @c sm_connect(). If
+ * everything is successful, it stores libSM connection object in the global
+ * variable @c smcon and calls @c sm_store_props() and @c sm_set_contant_props().
+ *
+ * This function can be called at any time during Geany instance's lifetime.
+ * Actually it is called when Geany is starting.
+ *
+ * When Geany is compiled without XSMP support, this function is a no-op.
+ */
+void sm_init(const char * argv0, const char * libsm_client_id)
+{
+ #ifdef HAVE_LIBSM
+
+ char * new_client_id;
+ SmcConn smcon = sm_connect(libsm_client_id, &new_client_id);
+
+ if (!smcon)
+ return;
+
+ sm_store_props(argv0, new_client_id);
+ sm_set_constant_props(smcon);
+ free(new_client_id);
+ #endif
+}
+
+/** @} */
+
+#ifdef HAVE_LIBSM
+
+/**
+ * @name ICE support
+ * @{
+ */
+
+/**
+ * ICE connection watcher used to attach a GIOChannel to each ICE connection
+ * so that this connection can be handled in GTK main loop.
+ *
+ * @param icecon ICE connection.
+ * @param client_data Client data specified in @c IceAddConnectionWatch() function call.
+ * Currently it is not used.
+ * @param opening Whether @c icecon is opening or closing.
+ * @param watch_data A piece of data that can be set when @c icecon is opening and
+ * read when it is closing. We store GIOChannel watcher ID here.
+ *
+ * We attach @c ice_iochannel_watch GIOChannel watcher to every created GIOChannel
+ * in order to handle messages. This is how session manager communicates to Geany.
+ *
+ * @see sm_connect()
+ */
+static void ice_connection_watch(IceConn icecon, IcePointer client_data,
+ Bool opening, IcePointer *watch_data)
+{
+ guint input_id;
+
+ if (opening)
+ {
+ 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_iochannel_watch, 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);
+ }
+}
+
+
+/**
+ * A watcher attached to a GIOChannel corresponding to an ICE connection.
+ *
+ * @param source A GIOChannel corresponding to an ICE connection.
+ * @param condition
+ * @param data Client data specified in @c g_io_add_watch() function call.
+ * An ICE connection object (of type @c IceConn) is stored here.
+ * @return Return FALSE to remove the GIOChannel.
+ *
+ * This function calls @c IceProcessMessages causing an appropriate libSM
+ * callback to be invoked.
+ *
+ * @see ice_connection_watch()
+ */
+static gboolean ice_iochannel_watch(GIOChannel * source, GIOCondition condition, gpointer data)
+{
+ IceConn icecon = (IceConn)data;
+ IceProcessMessages(icecon, NULL, NULL);
+ return TRUE;
+}
+
+/**
+ * @}
+ * @name libSM support implementation
+ * @{
+ */
+
+/**
+ * Connect to the session manager.
+ *
+ * @param libsm_client_id LibSM client ID saved from the previous session
+ * or @c NULL if there was no previous session.
+ * @param[out] new_client_id Upon successful completion, this variable is
+ * filled with newly retrieved libSM client ID.
+ * The string should be freed with @c free() when
+ * it is no longer needed. If connection to the
+ * session manager fails, the variable is filled
+ * with @c NULL pointer.
+ * @return libSM connection object or @c 0 if connection to the session manager fails.
+ */
+static SmcConn sm_connect(const char * libsm_client_id, char ** new_client_id)
+{
+ static const SmcCallbacks callbacks = {
+ {sm_save_yourself_callback, NULL},
+ {sm_die_callback, NULL},
+ {sm_save_complete_callback, NULL},
+ {sm_shutdown_cancelled_callback, NULL}};
+
+ gchar err[256] = "";
+ SmcConn smcon;
+
+ if (!g_getenv("SESSION_MANAGER"))
+ return 0;
+
+ IceAddConnectionWatch(ice_connection_watch, NULL);
+
+ smcon = (gpointer)SmcOpenConnection(NULL, NULL,
+ SmProtoMajor, SmProtoMinor,
+ SmcSaveYourselfProcMask |
+ SmcDieProcMask |
+ SmcSaveCompleteProcMask |
+ SmcShutdownCancelledProcMask,
+ (SmcCallbacks *)&callbacks,
+ (char *)libsm_client_id, new_client_id,
+ 256, err);
+
+ if (!smcon)
+ {
+ g_warning("While connecting to session manager:\n%s.", err);
+
+ IceRemoveConnectionWatch(ice_connection_watch, NULL);
+ return 0;
+ }
+
+ return smcon;
+}
+
+
+/**
+ * Store global @c SmPropValue objects.
+ *
+ * @param argv0 Value of @c argv[0].
+ * @param libsm_client_id LibSM client ID saved from the previous session or
+ * @c NULL if there was no previous session.
+ *
+ * This function stores @c SmPropValue objects which are initialized using
+ * arguments passed to @c sm_init() and used in @c sm_set_runtime_props().
+ * The objects are stored in the global scope.
+ *
+ * Function parameters correspond to the parameters of @c sm_init().
+ *
+ * @see sm_client_id_arg_val
+ * @see sm_program_val
+ */
+static void sm_store_props(const char * argv0, const char * libsm_client_id)
+{
+ const char * executable_path;
+ const char * client_id_arg;
+
+ /*
+ * 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).
+ */
+ if (strchr(argv0, G_DIR_SEPARATOR) == NULL)
+ executable_path = argv0;
+ else
+ {
+ gchar * curdir = g_get_current_dir();
+ executable_path = g_build_filename(curdir, argv0, NULL); /* never freed */
+ g_free(curdir);
+ }
+ sm_program_val.length = strlen(executable_path);
+ sm_program_val.value = (char *)executable_path;
+
+ client_id_arg = g_strconcat("--libsm-client-id=", libsm_client_id, NULL); /* never freed */
+ sm_client_id_arg_val.length = strlen(client_id_arg);
+ sm_client_id_arg_val.value = (char *)client_id_arg;
+}
+
+
+/**
+ * Set libSM properties which values are not changed during Geany's lifetime.
+ *
+ * @param smcon LibSM connection object.
+ *
+ * This function can be called at any time during Geany's lifetime.
+ * Actually it is called from @c sm_init().
+ *
+ * The function requires @c SmPropValue objects stored in @c sm_store_props(),
+ * so the caller must call the latter function before it calls this one.
+ */
+static void sm_set_constant_props(SmcConn smcon)
+{
+ 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);
+
+ g_free(curdir);
+}
+
+
+/**
+ * Set libSM properties which values can be modified during Geany's lifetime.
+ *
+ * @param smcon LibSM connection object.
+ *
+ * This function should be called as late as possible in order to avoid a
+ * situation when values are modified after they are stored and the stored
+ * values become incorrect.
+ *
+ * The function requires @c SmPropValue objects stored in @c sm_store_props(),
+ * so the caller must call the latter function before it calls this one.
+ */
+static void sm_set_runtime_props(SmcConn smcon)
+{
+ /*
+ * 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;
+
+ GArray * arr;
+ gint arr_real_len;
+
+ SmProp restart_command_prop, clone_command_prop;
+ SmPropValue * val;
+ SmProp * prop;
+
+
+ /*
+ * 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'.
+ */
+ arr = g_array_sized_new(FALSE, FALSE, sizeof(SmPropValue), page_count+4);
+ arr_real_len = 0;
+
+ ((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;
+ }
+ }
+
+ restart_command_prop.name = SmRestartCommand;
+ restart_command_prop.type = SmLISTofARRAY8;
+ restart_command_prop.num_vals = arr_real_len;
+ restart_command_prop.vals = (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;
+
+ clone_command_prop.name = SmCloneCommand;
+ clone_command_prop.type = SmLISTofARRAY8;
+ clone_command_prop.num_vals = arr_real_len - 1;
+ clone_command_prop.vals = (SmPropValue *)arr->data + 1;
+ prop = &clone_command_prop;
+ SmcSetProperties(smcon, 1, &prop);
+
+ g_array_free(arr, TRUE);
+}
+
+/**
+ * @}
+ * @name libSM callbacks
+ * @{
+ */
+
+/**
+ * "Save Yourself" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ * @param save_type Specifies the type of information that should be saved.
+ * @param shutdown Specifies if a shutdown is taking place.
+ * @param interact_style The type of interaction allowed with the user.
+ * @param fast If True, the client should save its state as quickly as possible.
+ *
+ * See libSM documentation for more details.
+ *
+ * Here we save Geany's configuration. If there are any unsaved documents and
+ * interaction with the user is allowed, we also make an "Interact Request" so
+ * that session manager sends us an "Interact" message. Otherwise, we are ready
+ * to handle "Die" or "Shutdown Cancelled" message.
+ *
+ * @see sm_interact_callback()
+ * @see sm_shutdown_cancelled_callback()
+ * @see sm_die_callback()
+ */
+static void sm_save_yourself_callback(SmcConn smcon, SmPointer client_data,
+ int save_type, Bool shutdown, int interact_style, Bool fast)
+{
+ if (save_type == SmSaveGlobal || save_type == SmSaveBoth)
+ {
+ if (interact_style == SmInteractStyleAny && document_any_unsaved())
+ {
+ if (!SmcInteractRequest(smcon, SmDialogNormal, sm_interact_callback, NULL))
+ SmcSaveYourselfDone(smcon, FALSE);
+ return;
+ }
+ else
+ main_save(FALSE);
+ }
+
+ /*
+ * 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_runtime_props(smcon);
+ SmcSaveYourselfDone(smcon, TRUE);
+}
+
+
+/**
+ * "Interact" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * The session manager sends us an "Interact" message after we make an
+ * "Interact Request" in @c sm_save_yourself_callback(). Here we are allowed to
+ * interact with the user, so we ask her whether to save changed documents
+ * (the user also can cancel the shutdown). After that we are ready to handle
+ * "Shutdown Cancelled" or "Die" message.
+ *
+ * @see sm_shutdown_cancelled_callback()
+ * @see sm_die_callback()
+ */
+static void sm_interact_callback(SmcConn smcon, SmPointer client_data)
+{
+ gboolean cancelled = !main_save(TRUE);
+ sm_set_runtime_props(smcon);
+ SmcInteractDone(smcon, cancelled);
+}
+
+
+/**
+ * "Save Complete" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * We are required to have this callback, but actually it is a no-op.
+ */
+static void sm_save_complete_callback(SmcConn smcon, SmPointer client_data)
+{
+}
+
+
+/**
+ * "Shutdown Cancelled" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * We are required to have this callback, but actually it is a no-op.
+ */
+static void sm_shutdown_cancelled_callback(SmcConn smcon, SmPointer client_data)
+{
+ SmcSaveYourselfDone(smcon, TRUE);
+}
+
+
+/**
+ * "Die" callback.
+ *
+ * @param smcon LibSM connection object.
+ * @param client_data Client data specified when the callback was registered.
+ * Currently it is not used.
+ *
+ * See libSM documentation for more details.
+ *
+ * The session manager asks us to quit Geany and we do it.
+ */
+static void sm_die_callback(SmcConn smcon, SmPointer client_data)
+{
+ SmcCloseConnection(smcon, 0, NULL);
+ main_finalize();
+}
+
+/** @} */
+
+#endif
diff --git a/src/sm.h b/src/sm.h
new file mode 100644
index 0000000..563c249
--- /dev/null
+++ b/src/sm.h
@@ -0,0 +1,28 @@
+/*
+ * sm.h - this file is part of Geany, a fast and lightweight IDE
+ *
+ * Copyright 2009 Eugene Arshinov <earshinov(at)gmail(dot)com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $Id$
+ */
+
+#ifndef GEANY_SM_H
+#define GEANY_SM_H 1
+
+void sm_init(const char * argv0, const char * libsm_client_id);
+
+#endif
diff --git a/wscript b/wscript
index b219bd2..0e102a5 100644
--- a/wscript
+++ b/wscript
@@ -109,7 +109,7 @@ geany_sources = [
'src/highlighting.c', 'src/interface.c', 'src/keybindings.c',
'src/keyfile.c', 'src/log.c', 'src/main.c', 'src/msgwindow.c', 'src/navqueue.c', 'src/notebook.c',
'src/plugins.c', 'src/pluginutils.c', 'src/prefix.c', 'src/prefs.c', 'src/printing.c', 'src/project.c',
- 'src/sciwrappers.c', 'src/search.c', 'src/socket.c', 'src/stash.c',
+ 'src/sciwrappers.c', 'src/search.c', 'src/sm.c', 'src/socket.c', 'src/stash.c',
'src/symbols.c',
'src/templates.c', 'src/toolbar.c', 'src/tools.c', 'src/sidebar.c',
'src/ui_utils.c', 'src/utils.c' ]
--
1.6.5.5
>From 72f2294021b02964d3978fdb9c7c6834e335a585 Mon Sep 17 00:00:00 2001
From: Eugene Arshinov <[email protected]>
Date: Sun, 13 Dec 2009 15:01:02 +0300
Subject: [PATCH 2/7] Add sm_finalize().
---
src/main.c | 2 ++
src/sm.c | 43 ++++++++++++++++++++++++++++++++++++-------
src/sm.h | 2 ++
3 files changed, 40 insertions(+), 7 deletions(-)
diff --git a/src/main.c b/src/main.c
index db7521d..4ed369f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1122,6 +1122,8 @@ void main_finalize()
project_close(FALSE, FALSE);
document_close_all(TRUE);
+ sm_finalize();
+
#ifdef HAVE_SOCKET
socket_finalize();
#endif
diff --git a/src/sm.c b/src/sm.c
index 0112274..a6ad6bd 100644
--- a/src/sm.c
+++ b/src/sm.c
@@ -27,7 +27,8 @@
* In order to support XSMP, we have to support Inter-Client Exchange
* Protocol (ICE). This file takes care of the latter too.
*
- * Typical usage: @c sm_init() is called when Geany is starting.
+ * Typical usage: @c sm_init() is called when Geany is starting and
+ * @c sm_finalize() is called when Geany is quitting.
*
* According to libSM documentation, client should retain the same ID after
* it is restarted. The main module (@c main.c) maintains "--libsm-client-id"
@@ -78,6 +79,9 @@ static void sm_shutdown_cancelled_callback(SmcConn smcon, SmPointer client_data)
static void sm_die_callback(SmcConn smcon, SmPointer client_data);
+/** LibSM connection object initialized in @c sm_init() and used in @c sm_finalize(). */
+static SmcConn smc_conn;
+
/**
* @c SmPropValue storing a path to Geany's executable.
*
@@ -123,19 +127,43 @@ static SmPropValue sm_client_id_arg_val;
void sm_init(const char * argv0, const char * libsm_client_id)
{
#ifdef HAVE_LIBSM
-
char * new_client_id;
- SmcConn smcon = sm_connect(libsm_client_id, &new_client_id);
- if (!smcon)
+ /* This function should be called once */
+ g_assert(!smc_conn);
+ if (smc_conn)
+ return;
+
+ smc_conn = sm_connect(libsm_client_id, &new_client_id);
+ if (!smc_conn)
return;
sm_store_props(argv0, new_client_id);
- sm_set_constant_props(smcon);
+ sm_set_constant_props(smc_conn);
free(new_client_id);
#endif
}
+
+/**
+ * Perform cleanup.
+ *
+ * Call this function when XSMP support is no longer needed. In fact it is
+ * called when Geany is quitting.
+ *
+ * When Geany is compiled without XSMP support, this function is a no-op.
+ */
+void sm_finalize(void)
+{
+ #ifdef HAVE_LIBSM
+ if (smc_conn)
+ {
+ SmcCloseConnection(smc_conn, 0, NULL);
+ smc_conn = 0;
+ }
+ #endif
+}
+
/** @} */
#ifdef HAVE_LIBSM
@@ -590,11 +618,12 @@ static void sm_shutdown_cancelled_callback(SmcConn smcon, SmPointer client_data)
*
* See libSM documentation for more details.
*
- * The session manager asks us to quit Geany and we do it.
+ * The session manager asks us to quit Geany and we do it. When quitting, the
+ * main module (@c main.c) will call @c sm_finalize() where we will close the
+ * connection to the session manager.
*/
static void sm_die_callback(SmcConn smcon, SmPointer client_data)
{
- SmcCloseConnection(smcon, 0, NULL);
main_finalize();
}
diff --git a/src/sm.h b/src/sm.h
index 563c249..d6eb578 100644
--- a/src/sm.h
+++ b/src/sm.h
@@ -25,4 +25,6 @@
void sm_init(const char * argv0, const char * libsm_client_id);
+void sm_finalize(void);
+
#endif
--
1.6.5.5
>From 4d5ecba0022dc7d974929d3077b9df5c8a3e518c Mon Sep 17 00:00:00 2001
From: Eugene Arshinov <[email protected]>
Date: Sun, 13 Dec 2009 15:03:03 +0300
Subject: [PATCH 3/7] Fix restart command.
---
src/sm.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/src/sm.c b/src/sm.c
index a6ad6bd..87f7072 100644
--- a/src/sm.c
+++ b/src/sm.c
@@ -320,6 +320,8 @@ static void sm_store_props(const char * argv0, const char * libsm_client_id)
*/
if (strchr(argv0, G_DIR_SEPARATOR) == NULL)
executable_path = argv0;
+ else if (g_path_is_absolute(argv0))
+ executable_path = argv0;
else
{
gchar * curdir = g_get_current_dir();
--
1.6.5.5
>From 682fcf279e5af7136f7bce0c395838dae9397456 Mon Sep 17 00:00:00 2001
From: Eugene Arshinov <[email protected]>
Date: Sun, 13 Dec 2009 15:04:32 +0300
Subject: [PATCH 4/7] Handle "Interact" message properly.
---
src/sm.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/src/sm.c b/src/sm.c
index 87f7072..6a6da9f 100644
--- a/src/sm.c
+++ b/src/sm.c
@@ -575,6 +575,7 @@ static void sm_interact_callback(SmcConn smcon, SmPointer client_data)
gboolean cancelled = !main_save(TRUE);
sm_set_runtime_props(smcon);
SmcInteractDone(smcon, cancelled);
+ SmcSaveYourselfDone(smcon, TRUE);
}
--
1.6.5.5
>From c015894539c275289216ecfa22934c5640a2e8ff Mon Sep 17 00:00:00 2001
From: Eugene Arshinov <[email protected]>
Date: Sun, 13 Dec 2009 14:32:14 +0300
Subject: [PATCH 5/7] Make --new-instance imply --no-session.
---
src/keyfile.c | 3 +--
src/main.c | 4 ++--
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/keyfile.c b/src/keyfile.c
index 7ae6225..264cd71 100644
--- a/src/keyfile.c
+++ b/src/keyfile.c
@@ -531,7 +531,7 @@ void configuration_save(void)
save_recent_files(config, ui_prefs.recent_queue, "recent_files");
save_recent_files(config, ui_prefs.recent_projects_queue, "recent_projects");
- if (cl_options.load_session)
+ if (cl_options.load_session && !cl_options.new_instance)
configuration_save_session_files(config);
/* write the file */
@@ -1097,4 +1097,3 @@ void configuration_finalize(void)
g_ptr_array_free(keyfile_groups, TRUE);
g_ptr_array_free(pref_groups, TRUE);
}
-
diff --git a/src/main.c b/src/main.c
index 4ed369f..1b195e7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -127,7 +127,7 @@ static GOptionEntry entries[] =
{ "generate-tags", 'g', 0, G_OPTION_ARG_NONE, &generate_tags, N_("Generate global tags file (see documentation)"), NULL },
{ "no-preprocessing", 'P', 0, G_OPTION_ARG_NONE, &no_preprocessing, N_("Don't preprocess C/C++ files when generating tags"), NULL },
#ifdef HAVE_SOCKET
- { "new-instance", 'i', 0, G_OPTION_ARG_NONE, &cl_options.new_instance, N_("Don't open files in a running instance, force opening a new instance"), NULL },
+ { "new-instance", 'i', 0, G_OPTION_ARG_NONE, &cl_options.new_instance, N_("Don't open files in a running instance, force opening a new instance. Implies --no-session"), NULL },
{ "socket-file", 0, 0, G_OPTION_ARG_FILENAME, &cl_options.socket_filename, N_("Use this socket filename for communication with a running Geany instance"), NULL },
#endif
{ "line", 'l', 0, G_OPTION_ARG_INT, &cl_options.goto_line, N_("Set initial line number for the first opened file"), NULL },
@@ -137,7 +137,7 @@ static GOptionEntry entries[] =
{ "no-plugins", 'p', 0, G_OPTION_ARG_NONE, &no_plugins, N_("Don't load plugins"), NULL },
#endif
{ "print-prefix", 0, 0, G_OPTION_ARG_NONE, &print_prefix, N_("Print Geany's installation prefix"), NULL },
- { "no-session", 's', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &cl_options.load_session, N_("Don't load the previous session's files"), NULL },
+ { "no-session", 's', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &cl_options.load_session, N_("Don't load and save session's files"), NULL },
#ifdef HAVE_VTE
{ "no-terminal", 't', 0, G_OPTION_ARG_NONE, &no_vte, N_("Don't load terminal support"), NULL },
{ "vte-lib", 0, 0, G_OPTION_ARG_FILENAME, &lib_vte, N_("Filename of libvte.so"), NULL },
--
1.6.5.5
>From d2e11417e01b4e6a39a0b9759e4a45fbb3b9d933 Mon Sep 17 00:00:00 2001
From: Eugene Arshinov <[email protected]>
Date: Sun, 13 Dec 2009 16:04:02 +0300
Subject: [PATCH 6/7] Handle --no-session properly.
The previous commit allows us to change --no-session command line option
handling back to normal. The issue was described in a FIXME section
inside src/sm.c, the section is now deleted.
---
src/sm.c | 83 +++++++++++++++++++++++--------------------------------------
1 files changed, 31 insertions(+), 52 deletions(-)
diff --git a/src/sm.c b/src/sm.c
index 6a6da9f..1b2bd15 100644
--- a/src/sm.c
+++ b/src/sm.c
@@ -389,44 +389,6 @@ static void sm_set_constant_props(SmcConn smcon)
*/
static void sm_set_runtime_props(SmcConn smcon)
{
- /*
- * 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;
@@ -440,19 +402,24 @@ static void sm_set_runtime_props(SmcConn smcon)
/*
* 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;
+ * * two elements for program name and client ID;
+ * * possibly one element for "--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'.
*/
arr = g_array_sized_new(FALSE, FALSE, sizeof(SmPropValue), page_count+4);
arr_real_len = 0;
- ((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;
+ ((SmPropValue *)arr->data)[arr_real_len++] = sm_program_val;
+ ((SmPropValue *)arr->data)[arr_real_len++] = sm_client_id_arg_val;
+
+ if (!cl_options.load_session)
+ {
+ val = ((SmPropValue *)arr->data) + arr_real_len++;
+ val->length = 2; /* length of "-s" */
+ val->value = "-s";
+ }
#ifdef HAVE_SOCKET
if (cl_options.new_instance)
@@ -462,19 +429,32 @@ static void sm_set_runtime_props(SmcConn smcon)
val->value = "-i";
}
#endif
+
/* TODO: handle other command-line options */
- for (page = 0; page < page_count; page++)
+ if (cl_options.load_session && !cl_options.new_instance)
{
- GeanyDocument * doc = document_get_from_page(page);
- if (doc->real_path)
+ /*
+ * Files will be restored by Geany session management facilities.
+ * NOTE: the condition matches the one inside configuration_save() function in keyfile.c.
+ */
+ }
+ else
+ {
+ /* specify file names in the command line */
+ for (page = 0; page < page_count; page++)
{
- val = ((SmPropValue *)arr->data) + arr_real_len++;
- val->length = strlen(doc->real_path);
- val->value = doc->real_path;
+ 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;
+ }
}
}
+
restart_command_prop.name = SmRestartCommand;
restart_command_prop.type = SmLISTofARRAY8;
restart_command_prop.num_vals = arr_real_len;
@@ -487,7 +467,6 @@ static void sm_set_runtime_props(SmcConn smcon)
* so "remove" the corresponding element from `arr'.
*/
((SmPropValue *)arr->data)[1] = sm_program_val;
-
clone_command_prop.name = SmCloneCommand;
clone_command_prop.type = SmLISTofARRAY8;
clone_command_prop.num_vals = arr_real_len - 1;
--
1.6.5.5
>From b977a37d92d51165d8048281d953a74cedfb6c17 Mon Sep 17 00:00:00 2001
From: Eugene Arshinov <[email protected]>
Date: Tue, 15 Dec 2009 13:16:47 +0300
Subject: [PATCH 7/7] Use `GeanyDocument.file_name's instead of `GeanyDocument.real_path's in
restart command like Geany session management facilities do.
---
src/sm.c | 48 ++++++++++++++++++++++++++++++++++++------------
1 files changed, 36 insertions(+), 12 deletions(-)
diff --git a/src/sm.c b/src/sm.c
index 1b2bd15..c5767e7 100644
--- a/src/sm.c
+++ b/src/sm.c
@@ -59,6 +59,7 @@
#include "main.h" /* for cl_options */
#include "ui_utils.h" /* access main_widgets.notebook to iterate over opened docs */
#include "document.h"
+#include "utils.h"
static void ice_connection_watch(IceConn icecon, IcePointer client_data, Bool opening,
@@ -390,26 +391,31 @@ static void sm_set_constant_props(SmcConn smcon)
static void sm_set_runtime_props(SmcConn smcon)
{
const gint page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(main_widgets.notebook));
- gint page;
GArray * arr;
- gint arr_real_len;
+ gint arr_real_len, arr_filenames_begin;
SmProp restart_command_prop, clone_command_prop;
SmPropValue * val;
SmProp * prop;
+ gint i;
/*
- * Allocate space for `page_count+4' elements:
+ * Allocate space for `page_count+5' elements:
* * two elements for program name and client ID;
* * possibly one element for "--no-session" option;
* * possibly one element for "--new-instance" option;
+ * * possible one element for "--" to separate options and filenames;
* * max `page_count' elements for file paths.
- * Store the number of actually used elements in `arr_real_len'.
+ * The number of actually used elements is saved in `arr_real_len'.
+ * Variable `arr_filenames_begin' stores the index in the array where
+ * the list of filenames begin. If there are no filenames in `arr',
+ * the value is -1.
*/
- arr = g_array_sized_new(FALSE, FALSE, sizeof(SmPropValue), page_count+4);
+ arr = g_array_sized_new(FALSE, FALSE, sizeof(SmPropValue), page_count + 5);
arr_real_len = 0;
+ arr_filenames_begin = -1;
((SmPropValue *)arr->data)[arr_real_len++] = sm_program_val;
((SmPropValue *)arr->data)[arr_real_len++] = sm_client_id_arg_val;
@@ -441,15 +447,29 @@ static void sm_set_runtime_props(SmcConn smcon)
}
else
{
- /* specify file names in the command line */
- for (page = 0; page < page_count; page++)
+ /* Specify file names in the command line */
+
+ val = ((SmPropValue *)arr->data) + arr_real_len++;
+ val->length = 2; /* length of "--" */
+ val->value = "--";
+
+ arr_filenames_begin = arr_real_len;
+
+ /*
+ * NOTE: compare this cycle with the one inside
+ * configuration_save_session_files() function in keyfile.c.
+ */
+ for (i = 0; i < page_count; i++)
{
- GeanyDocument * doc = document_get_from_page(page);
- if (doc->real_path)
+ GeanyDocument * doc = document_get_from_page(i);
+ if (doc && doc->real_path)
{
+ /* this string will be freed when `arr' is no longer needed */
+ gchar * locale_filename = utils_get_locale_from_utf8(doc->file_name);
+
val = ((SmPropValue *)arr->data) + arr_real_len++;
- val->length = strlen(doc->real_path);
- val->value = doc->real_path;
+ val->length = strlen(locale_filename);
+ val->value = locale_filename;
}
}
}
@@ -470,10 +490,14 @@ static void sm_set_runtime_props(SmcConn smcon)
clone_command_prop.name = SmCloneCommand;
clone_command_prop.type = SmLISTofARRAY8;
clone_command_prop.num_vals = arr_real_len - 1;
- clone_command_prop.vals = (SmPropValue *)arr->data + 1;
+ clone_command_prop.vals = ((SmPropValue *)arr->data) + 1;
prop = &clone_command_prop;
SmcSetProperties(smcon, 1, &prop);
+
+ if (arr_filenames_begin != -1)
+ for (i = arr_filenames_begin; i < arr_real_len; i++)
+ g_free(((SmPropValue *)arr->data)[i].value);
g_array_free(arr, TRUE);
}
--
1.6.5.5
_______________________________________________
Geany-devel mailing list
[email protected]
http://lists.uvena.de/cgi-bin/mailman/listinfo/geany-devel